[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅

1. 前言:在AI时代重新拾起源码的温暖

"在AI可以自动生成代码的今天,为什么还要读源码?因为理解原理才能让我们从代码的'使用者'变成'创造者'!"

最近AI的崛起确实让技术圈发生了翻天覆地的变化,博主之前的源码解析栏目也因此沉寂了一段时间。不过,在经历了更多生产问题复盘和真实架构设计的实战后,我愈发觉得:理解底层原理才是应对技术变革的不变法宝

今天,让我们重新点燃源码解析的热情!随着这两年工作的积累,我对这些基础框架有了更深刻的理解,可以为大家带来更多实际应用中的"避坑指南"。好消息是,今天的代码量很少,相信你喝杯咖啡的时间就能轻松掌握!

代码分支:https://github.com/yihuiaa/little-spring/tree/jdk-dynamic-proxy

2. 总体设计:AOP动态代理的"四重奏"

在开始代码之旅前,让我们先认识今天的"主演阵容":

核心组件总览

classDiagram class AopProxy { <<interface>> +getProxy() Object } class JdkDynamicAopProxy { -AdvisedSupport advised +getProxy() Object +invoke(Object, Method, Object[]) Object } class AdvisedSupport { -TargetSource targetSource -MethodInterceptor methodInterceptor -MethodMatcher methodMatcher +getter/setter methods } class TargetSource { -Object target +getTargetClass() Class[] +getTarget() Object } class MethodInterceptor { <<interface>> +invoke(MethodInvocation) Object } class ReflectiveMethodInvocation { -Object target -Method method -Object[] arguments +proceed() Object } JdkDynamicAopProxy ..|> AopProxy JdkDynamicAopProxy ..|> InvocationHandler JdkDynamicAopProxy --> AdvisedSupport AdvisedSupport --> TargetSource AdvisedSupport --> MethodInterceptor JdkDynamicAopProxy --> ReflectiveMethodInvocation MethodInterceptor --> ReflectiveMethodInvocation

各组件职责说明:

  • AopProxy:获取代理对象的抽象接口,定义了统一的代理创建标准
  • JdkDynamicAopProxy:基于JDK动态代理的具体实现,我们的"男主角"
  • TargetSource:被代理对象的"保镖",负责安全地封装目标对象
  • MethodInterceptor:方法拦截器,AOP Alliance的"标准公民",可以在方法执行前后插入自定义逻辑
  • AdvisedSupport:AOP配置的"大脑",协调各个组件协同工作

3. 新增依赖:欢迎AOP Alliance大家庭

在开始编码前,我们需要引入一个重要依赖:

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

这个依赖是什么来头?

AOP Alliance是一个为AOP(面向切面编程)提供标准接口的库,你可以把它想象成AOP世界的"联合国"——它定义了各个AOP框架都能理解的"官方语言",让不同的AOP实现能够和平共处、相互协作。

想象一下,如果没有这个标准,Spring AOP和Guice AOP就像两个说不同语言的人,根本无法交流!

4. 核心代码解析:深入AOP动态代理的内心世界

4.1 AdvisedSupport - AOP配置的"指挥中心"

package org.springframework.aop;

import org.aopalliance.intercept.MethodInterceptor;

/**
 * Spring AOP核心配置类 - 负责协调AOP代理的各个组件
 * @author yihui
 */
public class AdvisedSupport {
    /**
     * 目标对象源 - 封装被代理的目标对象
     * 就像电影的"选角导演",负责找到合适的演员(目标对象)
     */
    private TargetSource targetSource;

    /**
     * 方法拦截器 - 定义具体的增强逻辑
     * 相当于电影的"特效团队",在原有剧情前后添加炫酷特效
     */
    private MethodInterceptor methodInterceptor;

    /**
     * 方法匹配器 - 决定哪些方法需要被拦截
     * 就像"剧本编辑",决定哪些场景需要添加特效
     */
    private MethodMatcher methodMatcher;

    // getter和setter方法...
}

设计亮点:

  • 采用组合模式,将三个核心组件完美整合
  • 配置与执行分离,符合单一职责原则
  • 为后续扩展预留了充足空间

4.2 TargetSource - 目标对象的"贴心保镖"

package org.springframework.aop;

/**
 * 被代理的目标对象 - 采用不可变设计确保线程安全
 * @author yihui
 */
public class TargetSource {
    /**
     * 不可变的目标对象引用 - 一旦"签约"就不能更改
     */
    private final Object target;

    public TargetSource(Object target) {
        this.target = target;
    }

    /**
     * 返回目标对象实现的所有接口 - 为JDK动态代理提供"角色清单"
     */
    public Class<?>[] getTargetClass() {
        return this.target.getClass().getInterfaces();
    }

    public Object getTarget() {
        return this.target;
    }
}

为什么需要TargetSource?
想象一下,如果没有这个封装,每次需要目标对象时都要直接操作原始对象,就像没有经纪人的明星——既不够安全,也不够专业!

4.3 JdkDynamicAopProxy - 动态代理的"魔法师"

package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.AdvisedSupport;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理 - 巧妙融合AOP标准与JDK原生动态代理
 * @author yihui
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    /**
     * 创建代理对象 - 这里是魔法开始的地方!
     */
    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(
            getClass().getClassLoader(), 
            advised.getTargetSource().getTargetClass(), 
            this
        );
    }

    /**
     * 方法调用拦截 - 每个方法调用都要经过这里的"安检"
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检查这个方法是否需要被拦截(是否需要过安检)
        if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
            // 需要拦截:请拦截器来处理(走特殊通道)
            MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(
                advised.getTargetSource().getTarget(), method, args));
        }
        // 不需要拦截:直接放行(走普通通道)
        return method.invoke(advised.getTargetSource().getTarget(), args);
    }
}

双重身份的魅力:

  • AopProxy接口:对外提供统一的代理创建接口
  • InvocationHandler接口:对内处理方法调用的拦截逻辑

这种设计就像一个人既是"建筑设计师"(负责创建),又是"物业经理"(负责运营),确保了整个流程的连贯性。

JDK动态代理 vs CGLIB代理:

特性 JDK动态代理 CGLIB代理
基础 基于接口 基于类继承
依赖 JDK内置,无需额外依赖 需要CGLIB库
性能 JDK6+性能优秀 通常稍慢,但在持续优化
限制 只能代理接口方法 可以代理类,但final方法不行

实际开发中的"坑":

  1. 自调用问题:代理对象内部方法互相调用时,不会经过代理
public class UserService {
    public void updateUser() {
        this.validateUser(); // 这个调用不会走代理!
    }
}
  1. equals和hashCode:需要特殊处理,避免代理对象比较时出现意外结果

4.4 ReflectiveMethodInvocation - 方法调用的"时光胶囊"

package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;

/**
 * 方法调用上下文封装 - 把一次方法调用打包成"标准化包裹"
 * @author yihui
 */
public class ReflectiveMethodInvocation implements MethodInvocation {
    /**
     * 目标对象引用 - 要调用谁
     */
    private final Object target;

    /**
     * 方法元数据 - 要调用什么方法
     */
    private final Method method;

    /**
     * 方法参数 - 调用时传递什么参数
     */
    private final Object[] arguments;

    public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {
        this.target = target;
        this.method = method;
        this.arguments = arguments;
    }

    /**
     * 执行目标方法 - 打开"时光胶囊",执行原始逻辑
     */
    @Override
    public Object proceed() throws Throwable {
        return method.invoke(target, arguments);
    }

    // 其他信息获取方法...
    @Override
    public Method getMethod() { return method; }
    
    @Override
    public Object[] getArguments() { return arguments; }
    
    @Override
    public Object getThis() { return target; }
    
    @Override
    public AccessibleObject getStaticPart() { return method; }
}

为什么需要这个"时光胶囊"?
它把一次方法调用的所有上下文信息完整保存,让拦截器可以在任何时候、任何地方重现这次调用,就像把当下的瞬间封存在胶囊中,随时可以重新开启。

5. 实战测试:让代码"活"起来

理论说再多,不如实际跑一跑!让我们看看这些组件如何协同工作:

public class DynamicProxyTest {
    @Test
    public void testJdkDynamicProxy() throws Exception {
        // 1. 准备目标对象(我们的"演员")
        WorldService worldService = new WorldServiceImpl();

        // 2. 配置AOP(搭建"拍摄现场")
        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(worldService);
        WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
        MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* service.WorldService.sayHello(..))").getMethodMatcher();
        
        advisedSupport.setTargetSource(targetSource);
        advisedSupport.setMethodInterceptor(methodInterceptor);
        advisedSupport.setMethodMatcher(methodMatcher);

        // 3. 创建代理(开机!)
        WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        
        // 4. 使用代理(Action!)
        proxy.sayHello();
    }
}

// 业务接口
public interface WorldService {
    void sayHello();
}

// 业务实现
public class WorldServiceImpl implements WorldService {
    @Override
    public void sayHello() {
        System.out.println("Hello World");
    }
}

// 自定义拦截器
public class WorldServiceInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("方法处理前");
        Object result = invocation.proceed();
        System.out.println("方法处理后");
        return result;
    }
}

运行结果:

方法处理前
Hello World
方法处理后

看到这个输出,是不是有种"魔法生效"的成就感?我们的拦截器成功在目标方法执行前后添加了自定义逻辑!

6. 总结:从理解到掌握

通过今天的学习,我们不仅理解了JDK动态代理在Spring AOP中的应用,更重要的是,我们看到了一个优秀框架的设计思想:

  1. 标准化思维:通过AOP Alliance接口,确保与生态系统的兼容性
  2. 组合优于继承:通过AdvisedSupport组合各个组件,保持灵活性
  3. 职责分离:每个类都有明确的单一职责,便于理解和维护
  4. 扩展性设计:为后续功能升级预留了充足空间

记住这个精妙的AOP代理流程:

flowchart TD A[配置阶段] --> B[创建代理] B --> C[方法调用] subgraph A [配置阶段] A1[TargetSource] --> A2[AdvisedSupport] A3[MethodInterceptor] --> A2 A4[MethodMatcher] --> A2 end subgraph B [创建代理] B1[AdvisedSupport] --> B2[JdkDynamicAopProxy] B2 --> B3[代理对象] end subgraph C [方法调用] C1[代理对象] --> C2{MethodMatcher检查} C2 -->|匹配| C3[MethodInterceptor] C2 -->|不匹配| C4[直接调用目标方法] C3 --> C5[ReflectiveMethodInvocation] C5 --> C6[目标方法执行] end

虽然这只是Spring AOP的简化实现,但核心思想与完整版一脉相承。理解了这个基础版本,再去学习完整的Spring AOP源码,就会觉得"原来如此"!

源码阅读就像拼图游戏,一开始可能只见树木不见森林,但当所有碎片就位时,一幅精美的画卷就会呈现在眼前。希望今天的讲解能帮你找到几块关键的拼图!

在接下来的章节中,我们将继续探索AOP的更多奥秘,包括CGLIB代理、拦截器链等高级特性。敬请期待!

posted @ 2025-11-27 00:10  yihuiComeOn  阅读(0)  评论(0)    收藏  举报