循环依赖
循环依赖指的是循环引用,也就是两个或两个以上的bean相互持有对方,最终形成闭环依赖。
比如:A依赖于B,B依赖于C,C又依赖于A。
spring中的循环依赖的场景有:
- 构造器的循环依赖(spring中无法解决,只能避免)
- 属性的循环依赖
如何检测循环依赖
就是在bean创建的时候,给bean打标,加上状态。如果在递归依赖的时候,发现依赖对象正处于创建中的状态,那么就说明循环依赖了。
spring怎么解决循环依赖
spring的循环依赖的理论依据其实在基于java的引用传递,当获取对象的引用的时候,对象的属性是可以延后设置的(但构造器必须是在获取引用之前)。
spring的单例对象的初始化:
- createBeanInstance:实例化,调用对象的构造方法实例化对象
- populateBean:填充属性,bean的依赖属性填充
- InitializeBean:初始化,调用spring xml中的init方法
那么循环依赖主要发生在第一部分和第二部分。也就是构造器循环依赖和属性循环依赖。
spring为了解决单例的循环依赖问题,使用了三级缓存
1 | /** Cache of singleton objects: bean name --> bean instance */ |
三级缓存:
- singletonFactories:单例对象工厂的cache
- earlySingletonObjects:提前曝光的单例对象的cache
- singletonObjects:单例对象的cache
1 | protected Object getSingleton(String beanName, boolean allowEarlyReference) { |
- isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。)
- allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
创建bean的时候,首先现从一级缓存singletonObjects中获取。如果获取不到,且对象正在创建,就从二级缓存earlySingletonObjects中进行获取。如果还是获取不到且有对象的创建工厂,就从三级缓存中获取。在不报错的情况下,三级缓存获取到后,会从三级缓存中移除,放入到二级缓存中。
三级缓存是ObjectFactory,定义如下:
1 | public interface ObjectFactory<T> { |
addSingletonFactory发生在createBeanInstance之后,也就是说,单例对象已被创建了(调用了构造器)。虽然还没进行属性的赋值,但已经是一个对象了,可以根据应用在堆中定位到对象。放到二级缓存中提前让其他对象进行依赖和使用。由于java中对象的属性,可以在初始化之后进行赋值。所以这样一来,就解决属性的循环依赖问题。这也是无法解决构造器循环依赖的原因。在三级缓存面前,是先要调用构造器生成对象的。
1 | protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { |