Spring AOP自动选择代理方式:目标类实现接口时用JDK动态代理,否则用CGLIB;可通过proxy-target-class=true强制启用CGLIB。

Spring 在 AOP 中不是“手动选”代理方式,而是根据目标类的结构自动决策——核心判断依据就一个:目标类是否实现了接口。这个切换过程由 DefaultAopProxyFactory 完成,对开发者透明,但理解它能避免配置踩坑、性能误判和代理失效问题。
JDK 动态代理触发条件
当被增强的目标对象(比如一个 @Service 类)至少实现了一个接口时,Spring 默认启用 JDK 动态代理。
- 代理对象是接口的实现类,而非原类的子类;调用时通过
InvocationHandler.invoke()统一拦截 - 所有方法调用都经过反射,开销略高,但类加载快、内存占用小、无第三方依赖
- 即使目标类有多个接口,代理也会实现全部,保证类型兼容性
- 若目标类仅声明了接口但没被 Spring 扫描到(如接口未被
@Component标记),也可能退为 CGLIB
CGLIB 代理触发条件
当目标类没有实现任何接口,或显式配置强制使用 CGLIB 时,Spring 会生成其子类作为代理。
- 底层基于 ASM 操作字节码,生成继承自目标类的新类,重写非
final方法 - 方法调用走的是直接分派(非反射),运行时性能更高,但首次生成代理类较慢、占用更多元空间(Metaspace)
- 要求目标类不能是
final,方法也不能是final或private,否则无法覆盖 - Spring Boot 3+ 默认开启 CGLIB 支持,无需额外引入 jar;旧版本需确保
spring-core中已包含 CGLIB 模块
切换逻辑的关键细节
这个“自动切换”不是简单二选一,而是一套带兜底和干预能力的策略:
立即学习“Java免费学习笔记(深入)”;
-
proxy-target-class=true(Spring Boot 中为spring.aop.proxy-target-class=true)会强制所有代理走 CGLIB,无视接口存在与否 - 如果目标类实现了接口,但同时又标注了
@Scope("prototype")或涉及循环依赖解耦场景,Spring 可能仍倾向 CGLIB 以保证代理一致性 - 某些高级特性(如 @Async、@Cacheable)内部也依赖代理机制,它们的代理选择同样遵循这套规则
- Spring 6.x 对 CGLIB 的字节码生成做了深度优化,创建速度提升约 30%,缩小了与 JDK 代理在初始化阶段的差距
怎么验证当前用了哪种代理
最直接的方式是在切面中打印代理对象的类名:
- 如果是
com.sun.proxy.$ProxyXX—— JDK 动态代理 - 如果是
xxx$$EnhancerBySpringCGLIB$$xxxx—— CGLIB 代理 - 也可通过
org.springframework.aop.support.AopUtils工具类判断:AopUtils.isJdkDynamicProxy(obj)或AopUtils.isCglibProxy(obj)


















