Spring Boot中@Configuration与@Component引发循环依赖差异的Bean构造解析
@Configuration vs @Component:Bean构造流程差异导致循环依赖的原因解析
核心区别:@Configuration类是被代理的
Spring对标注了@Configuration的类会生成CGLIB代理,而@Component只是普通的Bean实例,这是两种场景行为天差地别的根本原因。
用@Configuration时的循环依赖触发全过程
- Spring启动时,先创建MessageSourceConfig的原始对象,紧接着要为它生成CGLIB代理。
- 执行
@PostConstruct方法:此时代理还没完全生成好,如果方法里间接调用了本类的messageSource()@Bean方法,代理会试图从容器里拿这个Bean——但容器里还没这个Bean呢。 - 执行
afterPropertiesSet方法:这个方法直接调用messageSource(),因为是代理类,这个调用会被Spring拦截,强制触发messageSourceBean的创建流程。 - 循环依赖炸了:创建
messageSource的时候,可能需要依赖MessageSourceConfig本身(比如@Bean方法里用到了配置类的属性),但此时MessageSourceConfig还卡在初始化流程里(代理没生成完、初始化方法还在跑),容器检测到循环依赖,而默认是禁用循环依赖的,直接抛出异常。
换成@Component后的正常运行流程
- Spring直接实例化MessageSourceConfig作为普通Bean,不会生成代理。
- 执行
@PostConstruct和afterPropertiesSet时,调用messageSource()就是普通的Java方法调用,只是返回一个new出来的对象,不会触发Spring的Bean创建逻辑。 - 等MessageSourceConfig完全初始化完成后,Spring才会通过后置处理器扫描类里的
@Bean注解,单独去创建messageSourceBean。这时候配置类已经是可用状态了,就算messageSource依赖它,也不会有循环依赖的问题。
关键细节提醒
- @Configuration的代理作用:所有@Bean方法的调用都会被代理拦截,确保返回的是容器里的单例Bean,而不是每次调用都new一个新对象。但这种拦截在Bean初始化阶段就会触发Bean创建,很容易搞出循环依赖。
- @Component下的@Bean处理:Spring是在Bean初始化完成后,才去扫描类里的@Bean方法并注册Bean定义,这时候调用@Bean方法就是普通方法,不会被拦截,也不会提前触发Bean创建。
内容的提问来源于stack exchange,提问作者PhongPhamIUH
相关产品推荐
相关产品推荐

