首先这个答案是否定的,Spring只是解决了单例模式下属性依赖的循环问题,Spring为了解决单例的循环依赖问题,使用了三级缓存。
什么是循环依赖?
循环依赖也就是循环引用,即两个或者两个以上的 Bean 互相持有对象,最终形成闭环。
graph LR; A[ClassA] -->|dependency| B[ClassB] B -->|dependency| C[ClassC] C -->|dependency| A
Spring 的解决方案:
/** Cache of singleton objects: bean name to bean instance. */ |
Spring 解决单例的循环依赖问题,使用到了 三级缓存:
- 第一级缓存 (singletonObjects):单例对象缓存池,已经实例化并且属性赋值,属于成熟对象;
- 第二级缓存 (earlySingletonObjects):单例对象缓存池,已经实例化但尚未属性赋值,属于半成品对象;
- 第三级缓存 (singletonFactories):单例工厂缓存,用于创建某个对象。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { |
isSingletonCurrentlyInCreation()
:判断当前单例bean是否正在建立中,也就是没有初始化完成。allowEarlyReference
:是否允许从singletonFactories中经过getObject拿到对象。
小结
分析 getSingleton()
的整个过程,Spring 首先从一级缓存
singletonObjects
中获取。若是获取不到,而且对象正在建立中,就再从二级缓存
earlySingletonObjects
中获取。若是仍是获取不到且容许
singletonFactories
经过 getObject()
获取,就从三级缓存singletonFactory.getObject()
获取,若是获取到了则从三级缓存移动到了二级缓存。
从上面三级缓存的分析,可以看出来,Spring解决循环依赖的诀窍就在于
singletonFactories
三级缓存。这个缓存的类型是
ObjectFactory
,定义:
|
在 bean
建立过程当中,有两处比较重要的匿名内部类实现了该接口。一处是 Spring
利用其建立bean
的时候,另外一处就是:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) |
此处就是解决循环依赖的关键,这段代码发生在
createBeanInstance
以后,也就是说单例对象此时已经被建立出来的。这个对象已经被生产出来了,虽然还不完美(尚未进行初始化的第二步和第三步),可是已经能根据对象引用能定位到堆中的对象,因此
Spring 此时将这个对象提早暴露出来,供后续使用。
Spring 为什么不能解决非单例属性之外的循环依赖
Spring 为什么不能解决构造器的循环依赖?
构造器注入形成的循环依赖:也就是 beanB 需要在 beanA 的构造函数中完成初始化,beanA 也需要在 beanB 的构造函数中完成初始化,根据 Spring Bean 的生命周期,这种情况的结果就是两个 bean 都不能完成初始化,循环依赖难以解决。
Spring 解决循环依赖主要是依赖三级缓存,但是在调用构造方法之前还未将对象对应的 ObjectFactory 放入三级缓存之中,因此后续的依赖调用构造方法的时候并不能从三级缓存中获取到依赖的 Bean,因此不能解决。
Spring 为什么不能解决作用域为 prototype 的 Bean 的循环依赖?
因为 Spring 不会缓存 「prototype」作用域的 bean,而 Spring 中循环依赖的解决正是通过缓存来实现的。
开发中自主解决循环依赖
1)生成代理对象产生的循环依赖
这类循环依赖问题解决方法很多,主要有:
- 使用
@Lazy
注解,延迟加载 - 使用
@DependsOn
注解,指定加载先后关系 - 修改文件名称,改变循环依赖类的加载顺序
2)构造器循环依赖
这类循环依赖问题可以通过使用 @Lazy
注解解决。
public A({ B b) |