手写Spring AOP,JDK动态代理如何邂逅AOP浪漫?

摘要:'在AI可以自动生成代码的今天,为什么还要读源码?因为理解原理才能让我们从代码的'使用者'变成'创造者'!'最近AI的崛起确实让技术
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方法不行 实际开发中的"坑": 自调用问题:代理对象内部方法互相调用时,不会经过代理 public class UserService { public void updateUser() { this.validateUser(); // 这个调用不会走代理! } } 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中的应用,更重要的是,我们看到了一个优秀框架的设计思想: 标准化思维:通过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代理、拦截器链等高级特性。敬请期待!