Spring CGLIB何时代理类?相关配置有哪些?
Spring CGLIB代理触发时机与配置调整
一、CGLIB代理的触发场景
- 目标类无接口实现:Spring默认会选择CGLIB生成子类代理,因为JDK动态代理仅支持基于接口的代理。
- 强制指定CGLIB代理:通过
@EnableAspectJAutoProxy(proxyTargetClass = true)(注解配置)或<aop:aspectj-autoproxy proxy-target-class="true"/>(XML配置),可强制Spring对所有Bean使用CGLIB代理,无视目标类是否有接口。 - 注解驱动的方法拦截需求:像你用到的
@Cacheable这类注解,Spring需要拦截方法调用以实现缓存逻辑。如果目标类无接口,或全局/缓存切面配置了强制CGLIB,就会生成CGLIB代理。另外,若存在类内部方法调用(比如类中A方法调用本类带@Cacheable的B方法),JDK代理无法拦截这类场景,Spring会自动切换为CGLIB代理来实现拦截——这很可能是你两个场景出现差异的原因。
二、可调整的代理配置
- 全局代理类型控制:
- 在配置类上添加
@EnableAspectJAutoProxy(proxyTargetClass = false)(默认值为false),Spring会优先使用JDK动态代理,仅当目标类无接口时才用CGLIB。设为true则强制所有代理用CGLIB。 - XML环境对应配置
<aop:aspectj-autoproxy proxy-target-class="false"/>。
- 在配置类上添加
- 缓存切面专属配置:
- 针对Spring Cache,可通过
@EnableCaching(proxyTargetClass = true/false)单独指定缓存切面的代理类型,该配置会覆盖全局代理设置。
- 针对Spring Cache,可通过
- 排除特定类的代理:
- 若需避免某个类被CGLIB代理,Spring Framework 6.1+可使用
@ProxyIgnore注解标记类;低版本可通过自定义BeanPostProcessor来拦截代理逻辑,跳过目标类的代理生成。
- 若需避免某个类被CGLIB代理,Spring Framework 6.1+可使用
关于你两个场景的配置差异排查
你说两个场景配置几乎一致,可能的隐性差异:
- 其中一个场景的Bean是否被其他Spring切面(如事务、日志切面)代理,多切面叠加可能触发CGLIB代理优先级提升。
- 是否存在内部方法调用的情况:一个场景是外部调用
@Cacheable方法,另一个是本类内部调用,后者会触发CGLIB代理来实现拦截。 - Bean初始化顺序不同:代理逻辑的触发时机差异导致最终生成的代理类型不同。
另外,你已经用到的AopProxyUtils.ultimateTargetClass()可以稳定获取原始类,也可以用ClassUtils.getUserClass(Object obj)达到同样效果,后者更简洁。
内容的提问来源于stack exchange,提问作者Stan
相关产品推荐
相关产品推荐

