P68 SpringBoot 2.0 为啥弃用 JDK 代理?CGLIB 才是 AOP 的神
面试题:Spring AOP 的 JDK 动态代理和 CGLIB 有什么区别?为什么 SpringBoot 2.0 默认用 CGLIB?
1. 两种代理方式
JDK 动态代理
- 基于接口,生成一个实现同一接口的代理类;
- 只代理接口方法,类本身的方法不代理;
- 目标类没有接口就无法使用;
- 原理:
Proxy.newProxyInstance+InvocationHandler。
CGLIB 代理
- 基于继承,生成目标类的子类作为代理;
- 代理类的方法(含非接口方法),不需要接口;
- 原理:字节码生成(ASM)增强类;
- final 类/ final 方法无法代理(不能继承/覆写)。
2. SpringBoot 2.0 的变更
- Spring Boot 1.x:默认
spring.aop.proxy-target-class=false,有接口时用 JDK 动态代理; - Spring Boot 2.0 起:默认
proxy-target-class=true,一律用 CGLIB(现在叫 Spring Objenesis + CGLIB); - 原因:
- 很多类没有接口(@Service/@Component 常直接实现,但 Controller/配置类等无接口),JDK 代理派不上用场,容易踩坑(注入报错、代理不生效);
- 统一用 CGLIB,行为一致,减少"为什么 AOP 不生效"的排查;
- CGLIB 性能经过优化,差距已不大。
3. 常见坑
- 自调用不走代理:
this.method()内部调用不会经过代理(AOP 不生效),要注入代理对象或拆类; - CGLIB 代理不能代理 final 方法;Spring 里
@Transactional等注解在 final 方法上可能失效; - JDK 代理注入的是接口类型,CGLIB 注入的是实现类类型(注意强转/类型声明);
- 代理对象与目标对象 equals/hashCode/类型不同(
instanceof行为要留意)。
4. 加分点
- 说"JDK 代理基于接口、CGLIB 基于继承"是最核心区别;
- 追问"Spring 怎么选":Spring 4+ 可以配置
proxy-target-class=true;Boot 2 默认 true; - 提到 AOP 的底层:BeanPostProcessor + ProxyFactory,根据条件决定 JDK/CGLIB;
- 补充性能:JDK 动态代理调用开销更低(反射优化后接近),CGLIB 创建代理类开销高、但运行时方法调用经过字节码增强。
一句话总结
JDK 代理只能代理接口、CGLIB 通过继承代理类;SpringBoot 2.0 默认 CGLIB 是为了统一行为、覆盖无接口类,避免 AOP 不生效;注意自调用和 final 方法两个经典坑。