为何调用自身@Transactional方法需自注入,@Bean方法却无需保证单例?
为什么@Configuration类的this是CGLIB代理,而@Transactional类内部的this是原生对象?
这两种场景虽都用到CGLIB,但Spring对它们的代理逻辑完全不同,核心差异在于代理的目标与存在形式:
一、@Configuration类的CGLIB代理逻辑
Spring对标注@Configuration的类,会直接生成CGLIB子类代理,并且容器中存在的就是这个代理实例——原生配置类实例不会直接暴露。
当你在配置类内部调用其他@Bean方法时,this指向的是CGLIB代理对象,代理会拦截该方法调用:
- 先检查IoC容器中是否已存在该Bean实例
- 若存在,直接返回容器中的单例Bean
- 若不存在,才执行原
@Bean方法创建Bean并放入容器
这种设计的目的是保证@Bean方法的调用符合Spring的单例语义,避免多次调用@Bean方法生成多份实例。
二、@Transactional的AOP代理逻辑
Spring声明式事务(及其他AOP增强)采用目标对象包装的代理模式:
- 原生业务类实例是真实的逻辑执行者
- CGLIB(或JDK动态代理)生成的代理对象持有原生实例的引用,增强逻辑(如事务控制)在代理对象的方法中实现,之后再调用原生实例的对应方法
外部通过依赖注入拿到的是代理对象,但类内部的this指向原生业务实例——因为内部方法调用是直接在原生对象上执行的,未经过代理层拦截,所以不会触发AOP增强逻辑。
三、核心差异总结
| 维度 | @Configuration的CGLIB代理 | @Transactional的AOP代理 |
|---|---|---|
| 代理目标 | 配置类本身 | 业务类的原生实例 |
| 容器中存在的实例 | 仅代理实例,原生实例被替换 | 同时存在代理实例与原生实例,代理持有原生实例 |
| this指向 | 始终为代理对象(内部/外部调用均如此) | 外部调用是代理,内部调用是原生对象 |
| 代理目的 | 保证@Bean方法的单例语义 | 为业务方法添加增强逻辑(如事务、日志) |
内容的提问来源于stack exchange,提问作者Kirill
相关产品推荐
相关产品推荐

