外观
Spring 循环依赖是怎么解决
三级缓存机制
- 一级缓存(
singletonObjects
):用于存放完全初始化的单例Bean
,即已经经历了完整的生命周期,所有依赖都已注入完成的Bean
。 - 二级缓存(
earlySingletonObjects
):存放早期暴露的单例Bean
,这些Bean
已经创建,但还未完成依赖注入等完整的初始化过程。 - 三级缓存(
singletonFactories
):存储单例Bean
的ObjectFactory
,用于创建早期暴露的Bean
,主要是为了处理AOP
相关的代理Bean
创建。
以 Bean A
和 Bean B
循环依赖为例,其处理流程如下:
- 实例化
Bean A
:Spring
开始创建Bean A
,通过构造函数或工厂方法实例化Bean A
,并将Bean A
的工厂对象放入三级缓存。 - 发现依赖
Bean B
:在填充Bean A
的属性时,发现依赖Bean B
,于是开始创建Bean B
。 - 实例化
Bean B
:Spring
为Bean B
创建实例,并将Bean B
的工厂对象放入三级缓存。 - 发现
Bean B
依赖Bean A
:在填充Bean B
的属性时,发现依赖Bean A
,此时Spring
从三级缓存中获取Bean A
的工厂对象,生成Bean A
的早期引用,并将其放入二级缓存,同时从三级缓存中移除Bean A
的工厂对象。 - 完成
Bean B
初始化:将Bean A
的早期引用注入到Bean B
中,Bean B
完成属性填充和初始化,成为一个完全初始化的Bean
,放入一级缓存。 - 完成
Bean A
初始化:Spring
将Bean B
从一级缓存中取出注入到Bean A
中,Bean A
完成属性填充和初始化,成为一个完全初始化的Bean
,放入一级缓存,并从二级缓存中移除Bean A
的早期引用。
其他方式
- 使用
@Lazy
注解:可以让Spring
延迟初始化某个Bean
,当一个Bean
被标记为@Lazy
,在注入时不会立即创建实例,而是创建一个代理对象,在真正使用这个Bean
时才会去初始化它,从而打破循环依赖。 - 使用
Setter/Field
注入:使用Setter
注入或Field
注入代替构造器注入,Spring
在创建Bean
时先实例化Bean
,然后再处理依赖注入,这样可以在一定程度上避免循环依赖问题,因为依赖的注入是在Bean
创建完成后进行的,而不是在创建之前就必须确定所有依赖。 - 重构代码:对代码进行重新设计,消除循环依赖。比如将相互依赖的部分提取到一个新的类或接口中,让原来相互依赖的类依赖于这个新的类或接口,从而打破循环。