开篇:你有没有想过这个问题?

老铁们,前面我们学了怎么用 AOP 给方法加耗时统计、加日志、加各种增强功能。用起来是真爽——写一个切面类,加几个注解,所有方法都自动被增强了。

但是,你有没有想过一个问题:Spring 到底是怎么做到“不修改源码就能增强方法”的?

这就像你买了一台冰箱,它本来只能制冷,突然有一天它自己学会了制冰、联网、播放音乐……你没拆开它,它怎么就多了这些功能?

答案就是:代理模式。今天我们就来揭开这个黑盒子,看看 Spring AOP 背后的“魔术”到底是怎么变的。


一、什么是代理模式?先讲个故事

1.1 没有代理的日子

假设你是一个房东,手里有套房子要出租。你自己带人看房、自己谈价格、自己签合同、自己收租金……累不累?更麻烦的是,如果租客半夜马桶坏了,你也得自己去修。

这就是没有代理的情况:客户端(租客)直接访问目标对象(房东)

租客 → 房东(看房、签合同、收租、维修)

1.2 有了代理之后

后来你找了个中介。租客要找房子,先找中介;中介带看房、谈价格、签合同、收租金;出了事,租客也先找中介。你只需要安心收钱就行了。

这就是代理模式客户端不直接访问目标对象,而是通过一个代理类来间接访问

租客 → 中介(代理) → 房东(目标)

中介帮你干了所有杂活,但你(房东)的核心业务(出租房子)一点都没变。这就是代理模式的核心价值:在不修改目标对象的前提下,对目标对象的功能进行增强或控制


二、代理模式在AOP中的角色

把上面的例子对应到 AOP:

生活中的角色 AOP 中的角色 说明
房东 目标对象(Target) 真正干活的业务方法
租客 客户端(Client) 调用业务方法的代码
中介 代理对象(Proxy) 在目标方法前后干杂活的切面
租房合同 接口(Interface) 规定房东和中介都要做的事

画个图就清楚了

在这里插入图片描述


三、代理模式的两种实现方式

代理模式分为两种:静态代理动态代理。它们的区别就像“提前找好中介”和“临时找个中介”。

3.1 静态代理——提前写好中介合同

定义:在程序运行前,代理类的 .class 文件就已经存在了。也就是说,你是手动写的代理类

生活例子:你提前找好了一个中介,签了合同,以后所有租房业务都通过他。这个中介是固定存在的,不是临时找的。

代码示例(以租房为例):

// 1. 定义接口(房东和中介都要遵守的规矩)
public interface HouseSubject {
    void rentHouse();
}

// 2. 目标对象(房东)
public class RealHouseSubject implements HouseSubject {
    @Override
    public void rentHouse() {
        System.out.println("我是房东,我出租房子");
    }
}

// 3. 代理对象(中介)
public class HouseProxy implements HouseSubject {
    private HouseSubject target;  // 被代理的房东
    
    public HouseProxy(HouseSubject target) {
        this.target = target;
    }
    
    @Override
    public void rentHouse() {
        System.out.println("中介:开始代理,带人看房");
        target.rentHouse();  // 房东出租房子
        System.out.println("中介:代理结束,收中介费");
    }
}

// 4. 使用
public class Main {
    public static void main(String[] args) {
        HouseSubject landlord = new RealHouseSubject();
        HouseProxy proxy = new HouseProxy(landlord);
        proxy.rentHouse();  // 通过中介租房
    }
}

运行结果

中介:开始代理,带人看房
我是房东,我出租房子
中介:代理结束,收中介费

静态代理的缺点

  • 如果房东新增了“卖房子”的业务,接口要改,房东要改,中介也要改。
  • 如果来了个新房东(新的被代理对象),又要写一个新的代理类。
  • 代码写死,不灵活,维护成本高。

所以,静态代理在日常开发中几乎不用。


3.2 动态代理——运行时临时找个中介

定义:在程序运行时,动态创建代理对象。你不需要手动写代理类,JDK 或 CGLIB 帮你自动生成。

生活例子:你不是提前找好中介,而是每次有租客来看房时,临时打电话叫一个中介过来。这个中介是“现找现用”的。

动态代理有两种实现方式:JDK 动态代理CGLIB 动态代理


四、JDK 动态代理——只能代理有接口的类

4.1 工作原理

JDK 动态代理要求目标对象必须实现接口。它会在运行时,根据你给的接口,动态生成一个代理类。

三步走

  1. 定义一个接口(目标对象要实现它)。
  2. 写一个 InvocationHandler,在里面写增强逻辑。
  3. Proxy.newProxyInstance() 创建代理对象。

4.2 代码示例

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

// 1. 接口
public interface HouseSubject {
    void rentHouse();
}

// 2. 目标对象(房东)
public class RealHouseSubject implements HouseSubject {
    @Override
    public void rentHouse() {
        System.out.println("我是房东,我出租房子");
    }
}

// 3. 自定义 InvocationHandler(增强逻辑写在这里)
public class JDKInvocationHandler implements InvocationHandler {
    private Object target;  // 被代理的对象
    
    public JDKInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK代理:开始代理");
        Object result = method.invoke(target, args);  // 调用原方法
        System.out.println("JDK代理:代理结束");
        return result;
    }
}

// 4. 使用
public class Main {
    public static void main(String[] args) {
        HouseSubject landlord = new RealHouseSubject();
        
        // 动态创建代理对象
        HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
            landlord.getClass().getClassLoader(),  // 类加载器
            landlord.getClass().getInterfaces(),   // 要实现的接口
            new JDKInvocationHandler(landlord)     // 增强逻辑
        );
        
        proxy.rentHouse();
    }
}

运行结果

JDK代理:开始代理
我是房东,我出租房子
JDK代理:代理结束

4.3 JDK 动态代理的优缺点

优点 缺点
不需要手动写代理类,自动生成 只能代理实现了接口的类
代码灵活,增强逻辑统一 如果目标类没有接口,无法使用

五、CGLIB 动态代理——没有接口也能代理

5.1 为什么需要 CGLIB?

JDK 动态代理有个致命问题:目标类必须实现接口。但很多时候,我们的业务类可能没有接口(比如直接写的 @Service@Controller)。怎么办?

CGLIB(Code Generation Library)解决了这个问题。它通过继承目标类来生成代理子类,所以不需要接口。

生活例子:你找个中介,但房东没有签任何合同(没有接口)。CGLIB 的做法是:直接“认”这个房东当干爹,然后以“干儿子”的身份去帮他出租房子。

5.2 工作原理

CGLIB 会在运行时,动态生成目标类的子类,在子类中重写父类的方法,加入增强逻辑。

三步走

  1. 写一个 MethodInterceptor,在里面写增强逻辑。
  2. Enhancer.create() 创建代理对象。

5.3 代码示例

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 目标对象(没有接口!)
public class RealHouseSubject {
    public void rentHouse() {
        System.out.println("我是房东,我出租房子");
    }
}

// 2. 自定义 MethodInterceptor
public class CGLIBInterceptor implements MethodInterceptor {
    private Object target;
    
    public CGLIBInterceptor(Object target) {
        this.target = target;
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB代理:开始代理");
        Object result = proxy.invoke(target, args);  // 调用原方法
        System.out.println("CGLIB代理:代理结束");
        return result;
    }
}

// 3. 使用
public class Main {
    public static void main(String[] args) {
        RealHouseSubject landlord = new RealHouseSubject();
        
        // 动态创建代理对象(通过继承)
        RealHouseSubject proxy = (RealHouseSubject) Enhancer.create(
            landlord.getClass(),           // 目标类的类型
            new CGLIBInterceptor(landlord) // 增强逻辑
        );
        
        proxy.rentHouse();
    }
}

运行结果

CGLIB代理:开始代理
我是房东,我出租房子
CGLIB代理:代理结束

5.4 CGLIB 的优缺点

优点 缺点
不需要接口,可以代理普通类 不能代理 final 类(因为 final 不能被继承)
性能较好 需要额外引入 CGLIB 依赖(Spring Boot 已内置)

六、JDK vs CGLIB:一张图看懂区别

在这里插入图片描述


七、Spring AOP 到底用哪种代理?

Spring AOP 的代理选择逻辑很简单:

  • 目标类实现了接口 → 默认使用 JDK 动态代理
  • 目标类没有实现接口 → 使用 CGLIB 动态代理

Spring Boot 2.x 开始,为了简化配置,默认强制使用 CGLIB 代理(不管你有没有接口)。这样更统一,也更方便。

如果你想改回去,可以在 application.properties 中配置:

spring.aop.proxy-target-class=false

但一般不建议改,因为 CGLIB 更通用。


八、结语:AOP 的学习到此结束

老铁们,到这儿,我们的 Spring AOP 系列就全部结束了。

我们一路走来:

  1. 入门篇:用 AOP 统计方法耗时,感受它的威力。
  2. 核心概念篇:把切点、连接点、通知、切面这些抽象概念变得具体化。
  3. 通知类型篇:搞清楚了五种通知的执行时机和顺序。
  4. 切点表达式篇:学会了用 execution@annotation 精准定位要增强的方法。
  5. 原理篇:揭开了 AOP 的神秘面纱,明白了它背后是动态代理在支撑。

AOP 的核心思想就一句话:把重复的、横跨多个模块的共性功能抽取出来,在不修改源码的情况下,统一增强。

掌握了 AOP,你就掌握了一种强大的代码复用和功能增强的能力。以后遇到“给所有方法加日志”“统计所有方法耗时”“统一权限校验”这类需求,你就能轻松搞定。

Spring AOP 就到这里,感谢你的陪伴!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐