☕ Spring AOP 源码剖析:代理模式选择机制揭秘

在 Spring AOP 的使用过程中,我们经常听说 Spring 会在 JDK 动态代理和 CGLIB 之间自动选择代理方式。但这个选择背后的机制是怎样的?Spring 到底依据什么来决定使用哪种方式?本文带你通过源码一探究竟。

一、JDK 动态代理 vs CGLIB

Spring AOP 实现的两种代理技术:

  • JDK 动态代理:要求目标类实现接口,通过反射代理接口方法,代理类是接口的实现类。

  • CGLIB 代理:直接继承目标类,通过字节码生成子类覆盖方法,适用于无接口场景。

这两种方式各有优劣。JDK 代理对接口友好,结构清晰;CGLIB 则能代理类中所有非 final 的方法,功能更强,但对类结构有更多限制。

二、Spring 是如何选择代理方式的?

2.1 入口类:ProxyFactory

Spring AOP 最核心的代理生成类是 org.springframework.aop.framework.ProxyFactory,它的创建流程可以简化为:

AdvisedSupport advisedSupport = new AdvisedSupport();
ProxyFactory proxyFactory = new ProxyFactory(advisedSupport);
Object proxy = proxyFactory.getProxy();

真正判断代理方式的逻辑在 DefaultAopProxyFactory#createAopProxy()

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("Target class must be available if proxyTargetClass set to true");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    } else {
        return new JdkDynamicAopProxy(config);
    }
}

2.2 代理选择逻辑精简

Spring 的代理选择逻辑,集中在 DefaultAopProxyFactory#createAopProxy() 方法中,其核心逻辑如下:

if (proxyTargetClass || optimize || 没有接口) {
    if (目标类是接口 || 目标类是代理类) {
        使用 JDK 动态代理;
    } else {
        使用 CGLIB;
    }
} else {
    使用 JDK 动态代理;
}

三、判断逻辑解析

1. 优先使用 JDK 动态代理的情况:

  • proxyTargetClass = false(默认)

  • 且有接口

  • 且没有设置 optimize = true

☑️ 默认情况下,Spring 倾向于使用 JDK 动态代理以保持接口隔离性。

2. 强制使用 CGLIB 的条件:

  • proxyTargetClass = true

  • 或设置了 optimize = true

  • 或没有任何接口(接口为空)

此时 Spring 会自动采用 ObjenesisCglibAopProxy,使用 CGLIB 生成代理。

四、SpringBoot 默认策略

在 Spring Boot 中,代理选择默认由配置项控制:

spring.aop.proxy-target-class=true
  • 设置为 true:强制使用 CGLIB

  • 设置为 false:遵循接口优先,使用 JDK 动态代理

五、最佳实践建议

  •  如果你实现了接口,建议使用 JDK 动态代理,接口隔离更清晰。

  •  如果你想代理类中非接口方法,或使用注解 @Transactional,建议开启 proxyTargetClass = true

  •  切记:CGLIB 基于继承方式,final 类或 final 方法不能被代理!

六、总结

Spring AOP 的代理选择机制本质上就是对 JDK 动态代理和 CGLIB 字节码增强的平衡选择。了解其判断逻辑不仅能帮助我们更好地调试 Spring 应用,还能避免因配置失误引发的事务失效、AOP 失效等“代理坑”。

 如果你遇到某个切面不起作用,不妨第一时间确认它是否被代理了,以及代理方式是否正确。

 知识点补充:

Objenesis 是什么?

spring 为了解决 CGLIB 创建对象时必须调用默认构造方法的问题,引入了 Objenesis,它可以 无参构造一个类实例,哪怕没有默认构造方法。这让 Spring 的代理创建更加灵活和强大。

Objenesis 与代理构造

Spring 使用 CGLIB 创建代理对象时,通常依赖 Objenesis 工具。它能在不调用构造函数的前提下创建对象实例,解决了 CGLIB 要求目标类有无参构造函数的问题。

因此即便你没有写构造函数,Spring 也能轻松帮你生成代理类,这正是 Spring AOP 强大的背后支撑之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值