找回密码
 立即注册
首页 业界区 业界 [源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AO ...

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

方方仪 2025-11-27 01:45:01
1. 前言:在AI时代重新拾起源码的温暖

"在AI可以自动生成代码的今天,为什么还要读源码?因为理解原理才能让我们从代码的'使用者'变成'创造者'!"
最近AI的崛起确实让技术圈发生了翻天覆地的变化,博主之前的源码解析栏目也因此沉寂了一段时间。不过,在经历了更多生产问题复盘和真实架构设计的实战后,我愈发觉得:理解底层原理才是应对技术变革的不变法宝
今天,让我们重新点燃源码解析的热情!随着这两年工作的积累,我对这些基础框架有了更深刻的理解,可以为大家带来更多实际应用中的"避坑指南"。好消息是,今天的代码量很少,相信你喝杯咖啡的时间就能轻松掌握!
代码分支:https://github.com/yihuiaa/little-spring/tree/jdk-dynamic-proxy
2. 总体设计:AOP动态代理的"四重奏"

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

classDiagram    class AopProxy {                +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 {                +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大家庭

在开始编码前,我们需要引入一个重要依赖:
  1. <dependency>
  2.     <groupId>aopalliance</groupId>
  3.     aopalliance</artifactId>
  4.     <version>1.0</version>
  5. </dependency>
复制代码
这个依赖是什么来头?
AOP Alliance是一个为AOP(面向切面编程)提供标准接口的库,你可以把它想象成AOP世界的"联合国"——它定义了各个AOP框架都能理解的"官方语言",让不同的AOP实现能够和平共处、相互协作。
想象一下,如果没有这个标准,Spring AOP和Guice AOP就像两个说不同语言的人,根本无法交流!
4. 核心代码解析:深入AOP动态代理的内心世界

4.1 AdvisedSupport - AOP配置的"指挥中心"
  1. package org.springframework.aop;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. /**
  4. * Spring AOP核心配置类 - 负责协调AOP代理的各个组件
  5. * @author yihui
  6. */
  7. public class AdvisedSupport {
  8.     /**
  9.      * 目标对象源 - 封装被代理的目标对象
  10.      * 就像电影的"选角导演",负责找到合适的演员(目标对象)
  11.      */
  12.     private TargetSource targetSource;
  13.     /**
  14.      * 方法拦截器 - 定义具体的增强逻辑
  15.      * 相当于电影的"特效团队",在原有剧情前后添加炫酷特效
  16.      */
  17.     private MethodInterceptor methodInterceptor;
  18.     /**
  19.      * 方法匹配器 - 决定哪些方法需要被拦截
  20.      * 就像"剧本编辑",决定哪些场景需要添加特效
  21.      */
  22.     private MethodMatcher methodMatcher;
  23.     // getter和setter方法...
  24. }
复制代码
设计亮点:

  • 采用组合模式,将三个核心组件完美整合
  • 配置与执行分离,符合单一职责原则
  • 为后续扩展预留了充足空间
4.2 TargetSource - 目标对象的"贴心保镖"
  1. package org.springframework.aop;
  2. /**
  3. * 被代理的目标对象 - 采用不可变设计确保线程安全
  4. * @author yihui
  5. */
  6. public class TargetSource {
  7.     /**
  8.      * 不可变的目标对象引用 - 一旦"签约"就不能更改
  9.      */
  10.     private final Object target;
  11.     public TargetSource(Object target) {
  12.         this.target = target;
  13.     }
  14.     /**
  15.      * 返回目标对象实现的所有接口 - 为JDK动态代理提供"角色清单"
  16.      */
  17.     public Class<?>[] getTargetClass() {
  18.         return this.target.getClass().getInterfaces();
  19.     }
  20.     public Object getTarget() {
  21.         return this.target;
  22.     }
  23. }
复制代码
为什么需要TargetSource?
想象一下,如果没有这个封装,每次需要目标对象时都要直接操作原始对象,就像没有经纪人的明星——既不够安全,也不够专业!
4.3 JdkDynamicAopProxy - 动态代理的"魔法师"
  1. package org.springframework.aop.framework;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.springframework.aop.AdvisedSupport;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Proxy;
  7. /**
  8. * JDK动态代理 - 巧妙融合AOP标准与JDK原生动态代理
  9. * @author yihui
  10. */
  11. public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
  12.     private final AdvisedSupport advised;
  13.     public JdkDynamicAopProxy(AdvisedSupport advised) {
  14.         this.advised = advised;
  15.     }
  16.     /**
  17.      * 创建代理对象 - 这里是魔法开始的地方!
  18.      */
  19.     @Override
  20.     public Object getProxy() {
  21.         return Proxy.newProxyInstance(
  22.             getClass().getClassLoader(),
  23.             advised.getTargetSource().getTargetClass(),
  24.             this
  25.         );
  26.     }
  27.     /**
  28.      * 方法调用拦截 - 每个方法调用都要经过这里的"安检"
  29.      */
  30.     @Override
  31.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  32.         // 检查这个方法是否需要被拦截(是否需要过安检)
  33.         if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
  34.             // 需要拦截:请拦截器来处理(走特殊通道)
  35.             MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
  36.             return methodInterceptor.invoke(new ReflectiveMethodInvocation(
  37.                 advised.getTargetSource().getTarget(), method, args));
  38.         }
  39.         // 不需要拦截:直接放行(走普通通道)
  40.         return method.invoke(advised.getTargetSource().getTarget(), args);
  41.     }
  42. }
复制代码
双重身份的魅力:

  • AopProxy接口:对外提供统一的代理创建接口
  • InvocationHandler接口:对内处理方法调用的拦截逻辑
这种设计就像一个人既是"建筑设计师"(负责创建),又是"物业经理"(负责运营),确保了整个流程的连贯性。
JDK动态代理 vs CGLIB代理:
特性JDK动态代理CGLIB代理基础基于接口基于类继承依赖JDK内置,无需额外依赖需要CGLIB库性能JDK6+性能优秀通常稍慢,但在持续优化限制只能代理接口方法可以代理类,但final方法不行实际开发中的"坑":

  • 自调用问题:代理对象内部方法互相调用时,不会经过代理
  1. public class UserService {
  2.     public void updateUser() {
  3.         this.validateUser(); // 这个调用不会走代理!
  4.     }
  5. }
复制代码

  • equals和hashCode:需要特殊处理,避免代理对象比较时出现意外结果
4.4 ReflectiveMethodInvocation - 方法调用的"时光胶囊"
  1. package org.springframework.aop.framework;
  2. import org.aopalliance.intercept.MethodInvocation;
  3. import java.lang.reflect.AccessibleObject;
  4. import java.lang.reflect.Method;
  5. /**
  6. * 方法调用上下文封装 - 把一次方法调用打包成"标准化包裹"
  7. * @author yihui
  8. */
  9. public class ReflectiveMethodInvocation implements MethodInvocation {
  10.     /**
  11.      * 目标对象引用 - 要调用谁
  12.      */
  13.     private final Object target;
  14.     /**
  15.      * 方法元数据 - 要调用什么方法
  16.      */
  17.     private final Method method;
  18.     /**
  19.      * 方法参数 - 调用时传递什么参数
  20.      */
  21.     private final Object[] arguments;
  22.     public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {
  23.         this.target = target;
  24.         this.method = method;
  25.         this.arguments = arguments;
  26.     }
  27.     /**
  28.      * 执行目标方法 - 打开"时光胶囊",执行原始逻辑
  29.      */
  30.     @Override
  31.     public Object proceed() throws Throwable {
  32.         return method.invoke(target, arguments);
  33.     }
  34.     // 其他信息获取方法...
  35.     @Override
  36.     public Method getMethod() { return method; }
  37.    
  38.     @Override
  39.     public Object[] getArguments() { return arguments; }
  40.    
  41.     @Override
  42.     public Object getThis() { return target; }
  43.    
  44.     @Override
  45.     public AccessibleObject getStaticPart() { return method; }
  46. }
复制代码
为什么需要这个"时光胶囊"?
它把一次方法调用的所有上下文信息完整保存,让拦截器可以在任何时候、任何地方重现这次调用,就像把当下的瞬间封存在胶囊中,随时可以重新开启。
5. 实战测试:让代码"活"起来

理论说再多,不如实际跑一跑!让我们看看这些组件如何协同工作:
  1. public class DynamicProxyTest {
  2.     @Test
  3.     public void testJdkDynamicProxy() throws Exception {
  4.         // 1. 准备目标对象(我们的"演员")
  5.         WorldService worldService = new WorldServiceImpl();
  6.         // 2. 配置AOP(搭建"拍摄现场")
  7.         AdvisedSupport advisedSupport = new AdvisedSupport();
  8.         TargetSource targetSource = new TargetSource(worldService);
  9.         WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
  10.         MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* service.WorldService.sayHello(..))").getMethodMatcher();
  11.         
  12.         advisedSupport.setTargetSource(targetSource);
  13.         advisedSupport.setMethodInterceptor(methodInterceptor);
  14.         advisedSupport.setMethodMatcher(methodMatcher);
  15.         // 3. 创建代理(开机!)
  16.         WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();
  17.         
  18.         // 4. 使用代理(Action!)
  19.         proxy.sayHello();
  20.     }
  21. }
  22. // 业务接口
  23. public interface WorldService {
  24.     void sayHello();
  25. }
  26. // 业务实现
  27. public class WorldServiceImpl implements WorldService {
  28.     @Override
  29.     public void sayHello() {
  30.         System.out.println("Hello World");
  31.     }
  32. }
  33. // 自定义拦截器
  34. public class WorldServiceInterceptor implements MethodInterceptor {
  35.     @Override
  36.     public Object invoke(MethodInvocation invocation) throws Throwable {
  37.         System.out.println("方法处理前");
  38.         Object result = invocation.proceed();
  39.         System.out.println("方法处理后");
  40.         return result;
  41.     }
  42. }
复制代码
运行结果:
  1. 方法处理前
  2. Hello World
  3. 方法处理后
复制代码
看到这个输出,是不是有种"魔法生效"的成就感?我们的拦截器成功在目标方法执行前后添加了自定义逻辑!
6. 总结:从理解到掌握

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

  • 标准化思维:通过AOP Alliance接口,确保与生态系统的兼容性
  • 组合优于继承:通过AdvisedSupport组合各个组件,保持灵活性
  • 职责分离:每个类都有明确的单一职责,便于理解和维护
  • 扩展性设计:为后续功能升级预留了充足空间
记住这个精妙的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代理、拦截器链等高级特性。敬请期待!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

7 天前

举报

懂技术并乐意极积无私分享的人越来越少。珍惜
6 天前

举报

您需要登录后才可以回帖 登录 | 立即注册