Skip to content

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 方法两个经典坑。

基于 VitePress 重建