Spring循环依赖
循坏依赖其实就是循坏引用,也就是两个或者两个以上的Bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖C,C又依赖与A。
Spring的解决循环依赖的理论基于Java的引用传递,当获取对象引用用时,对象的属性是可以延后设置的,但是构造器是在获取引用之前调用,所以无法解决构造器的循环依赖。构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,Spring采⽤的是提前暴露对象的⽅法,即将没有设置属性值的对象引用提前暴露。
Spring通过三级缓存机制来解决循环依赖问题。
三级缓存
Spring AbstratBeanFactory类从其父类DefaultSingletonBeanRegistry中继承了三级缓存,一级缓存类行为ConcurrentHashMap,二,三级缓存类行为HashMap:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
- 一级缓存 - singletonObjects :单例对象的缓存
- 二级缓存 - earlySingletonObjects:未完成的单例对象缓存(通过动态代理逻辑增强后的对象,但是属性没有初始化完成)
- 三级缓存 - singletonFactories:单例工厂的缓存
三级缓存运行流程
我们跟踪Spring Bean初始化流程去探究三级缓存运行流程:
1.直接进入AbstractAutowireCapableBeanFactory#doCreateBean方法,在实例创建完成后首先将其加入三级缓存。存入value类型为ObjectFactory 可以通过此类得到相应的Object。
2.对aBean进行属性填充,进入AbstractAutowireCapableBeanFactory#populateBean方法,在其中调用
applyPropertyValues方法处理属性。找到处理依赖BBean代码逻辑,进入方法BeanDefinitionValueResolver#resolveValueIfNecessary
3.resolveValueIfNecessary中调用BeanDefinitionValueResolver#resolveReference通过beanFactory.getBean()去获取bBean
4.获取bBean,进入bean的创建流程,在为bBean填充属性时,发现其依赖aBean所以会调用beanFactory.getBean()去获取aBean。经过调用会来到doGetBean,尝试在缓存中去获取aBean
5.首先从一级缓存中寻找,若一级缓存中未找到且Bean正在被创建,则再二级缓存中去寻找,接下来再去三级缓存中寻找到aBean的 objectFactory,并将aBean从三级缓存移入二级缓存。这里会调用存入三级缓存时的Lamda表达式,调用getEarlyBeanReference()对aBean进行动态代理增强。
6.得到aBean后,bBean完成属性填充,继续后续初始化流程。完成初始化后加入一级缓存,并清除三级缓存。
7.此时bBean创建完成,回到aBean创建流程,继续后续初始化流程,完成初始化后加入一级缓存,并清除二级缓存。
至此,循环依赖问题得到了解决。
Spring为什么要使用三级缓存
首先根据OOP思想,我们必须保证每个缓存池中的Bean状态一致。如果只采用一级缓存,缓存中将会存在未完成初始化和完成初始化两种状态的Bean。若只采用二级缓存,则无法完成动态代理,实现代理对象的复用。所以Spring采用三级缓存。