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

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

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

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

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

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

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

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

核心组件总览

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

各组件职责说明:

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

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

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

xml 复制代码
<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配置的"指挥中心"

java 复制代码
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 - 目标对象的"贴心保镖"

java 复制代码
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 - 动态代理的"魔法师"

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

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

java 复制代码
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. 实战测试:让代码"活"起来

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

java 复制代码
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代理流程:
方法调用 匹配 不匹配 MethodMatcher检查 代理对象 MethodInterceptor 直接调用目标方法 ReflectiveMethodInvocation 目标方法执行 创建代理 JdkDynamicAopProxy AdvisedSupport 代理对象 配置阶段 AdvisedSupport TargetSource MethodInterceptor MethodMatcher

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

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

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

相关推荐
测试开发Kevin1 小时前
小tip:换行符CRLF 和 LF 的区别以及二者在实际项目中的影响
java·开发语言·python
笨手笨脚の1 小时前
Redis: Thread limit exceeded replacing blocked worker
java·redis·forkjoin·thread limit
Lenyiin1 小时前
Linux 基础IO
java·linux·服务器
松☆1 小时前
Dart 核心语法精讲:从空安全到流程控制(3)
android·java·开发语言
编码者卢布2 小时前
【Azure Storage Account】Azure Table Storage 跨区批量迁移方案
后端·python·flask
编码者卢布2 小时前
【App Service】Java应用上传文件功能部署在App Service Windows上报错 413 Payload Too Large
java·开发语言·windows
q行2 小时前
Spring概述(含单例设计模式和工厂设计模式)
java·spring
好好研究3 小时前
SpringBoot扩展SpringMVC
java·spring boot·spring·servlet·filter·listener
毕设源码-郭学长3 小时前
【开题答辩全过程】以 高校项目团队管理网站为例,包含答辩的问题和答案
java