Spring 在什么情况下会选择 JDK 动态代理,什么情况下会选择 CGLIB?这个选择可以手动配置吗?

Spring AOP 的代理选择策略

Spring AOP 在决定使用哪种动态代理技术时,遵循一个清晰的决策流程。这个流程的默认行为在不同版本的 Spring 中有所演变。

1. 老版本 Spring / 传统 XML 配置

在早期的 Spring 版本或者你还在使用传统的 Spring XML 配置时,选择策略如下:

  • 如果目标对象实现了接口: 默认使用 JDK 动态代理
    • Spring 会创建一个实现了目标对象所有接口的代理类。
  • 如果目标对象没有实现任何接口: 只能使用 CGLIB
    • Spring 会创建一个继承自目标对象的子类作为代理。

这个策略的出发点是:既然有 Java 原生的 JDK 代理可用,就优先使用它,只有在 JDK 代理无法工作时(没有接口),才回退到使用第三方的 CGLIB。

2. Spring Boot (2.x 及以后)

从 Spring Boot 2.0 开始,为了解决一些潜在问题并统一框架行为,这个默认策略发生了变化:

  • 无论目标对象是否实现了接口,默认都统一使用 CGLIB。

为什么要做这个改变?
主要有以下几个原因:

  1. 解决方法自调用 AOP 失效问题

    • 如果使用 JDK 代理,代理对象和目标对象是两个不同的对象(代理实现了接口,目标对象是接口的实现类)。当在目标对象内部通过 this 调用其他方法时,这个调用是直接发生在目标对象内部的,完全绕过了代理对象,导致 AOP 失效。
    • 而使用 CGLIB,代理对象是目标对象的子类。即使目标对象实现了接口,Spring 也可以为类本身创建代理,这使得在某些情况下处理内部调用(需要特殊配置)变得更加一致和可行。
  2. 统一行为,减少困惑

    • 开发者不再需要去思考“我的类有没有实现接口,它的代理方式会是什么?”。默认都是 CGLIB,行为更加可预测。
  3. 性能差异已微不足道

    • 在早期的 JDK 版本中,CGLIB 的性能通常优于基于反射的 JDK 代理。但在现代 JVM 中,反射的性能得到了极大优化,两者的性能差距已经非常小,不再是选择代理方式的主要考虑因素。

这个选择可以手动配置吗?

答案是:可以!

你可以通过一个非常重要的配置项来手动控制 Spring AOP 的代理行为。

application.propertiesapplication.yml 文件中,可以设置 spring.aop.proxy-target-class 属性。

spring.aop.proxy-target-class
  • true (默认值)

    • 强制使用 CGLIB 代理。这是 Spring Boot 的默认设置。
    • 即使你的目标对象实现了接口,Spring 也会忽略接口,直接为你的类创建 CGLIB 代理(即创建子类)。
    • 优点:行为统一,能更好地处理某些内部调用场景。
  • false

    • 恢复到老版本的选择策略
    • 如果目标对象实现了接口,就使用 JDK 动态代理
    • 如果目标对象没有实现接口,才使用 CGLIB
    • 使用场景:在极少数情况下,你可能需要代理对象严格地只实现接口,或者遇到了 CGLIB 无法解决的特定问题(例如,需要代理一个 final 类,但它实现了接口,此时只能用 JDK 代理)。

配置示例:

application.properties

# Spring Boot 默认就是 true,通常无需显式配置
# spring.aop.proxy-target-class=true

# 如果你想在有接口时切换回 JDK 动态代理,可以设置为 false
spring.aop.proxy-target-class=false

application.yml

spring:
  aop:
    # true (默认) 表示使用 CGLIB, false 表示在有接口时优先使用 JDK 代理
    proxy-target-class: false 

总结表格

spring.aop.proxy-target-class目标对象实现了接口目标对象没有实现接口备注
true (默认值)CGLIBCGLIBSpring Boot 的默认行为,强制基于类代理。
falseJDK 动态代理CGLIB传统 Spring 的行为,优先基于接口代理。

理解这个配置项对于排查 AOP 相关问题,特别是 ClassCastException(例如,试图将代理对象强转为具体实现类而不是接口)和方法自调用失效问题,非常有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值