Spring解决循环依赖——三级缓存

225 阅读4分钟

一、Bean的生命周期

1.1.Bean生命周期相关知识

前置需要了解的知识,Bean的生命周期,需要了解请移步Bean的生命周期

image.png

二、使用一级缓存解决同一个类被多次创建的问题

2.1.无缓存Bean创建图解

@Component
public class BeanA {
    @Autowired
    private BeanB b;
}
@Component
public class BeanB {
    @Autowired
    private BeanA a;
}

image.png 在无缓存的情况下,我们看到BeanB创建了一个新的BeanA'注入,但是我们都知道Spring里面大部分的Bean都是单例的,不能创建出BeanA和BeanA'的情况,因此需要引入一级缓存,将创建好的BeanA给缓存起来方便使用。

2.2.同一个类不能创建多个Bean导致循环依赖的问题

image.png 由于BeanA没有创建完毕,在一级缓存中不存在,同时由于不能创建多个Bean,因此BeanB无法创建BeanA,BeanB的创建流程就因此中断。

总结:一级缓存的存在是为了解决同一个类被多次创建的问题,但是因此也存在循环依赖的问题。

三、使用二级缓存解决Bean循环依赖

3.1.二级缓存的使用图解

在实例化阶段的时候已经创建出A的对象了,只是没有填充A里面的属性,并非是完整的BeanA,一旦调用的对于的属性就会报错。但是似乎我们在bean创建的过程中并不需要调用其属性。那么我们如果我们能够提早引用非完整的BeanA,就能解决循环依赖的问题。

image.png

3.2.二级缓存存在的问题

image.png 当发生循环依赖,二级缓存命中,此时BeanB引用了非完整对象A,当BeanA完成了属性赋值和初始化后,BeanB引用的非完整BeanA就会变成完整的对象A

但是若BeanA在属性赋值和初始的过程换了一个新的对象A' 作为BeanA,那么BeanB引用的非完整的对象A就是一个错误的BeanA。

四、使用三级缓存拒绝部分循环依赖

4.1.仅发生循环依赖

image.png BeanA没有更换对象,不会报错

4.2.仅BeanA在属性赋值和初始化的过程更换对象

image.png BeanA从对象A更换为对象A' 但是由于没有被引用到非完整BeanA(发生循环依赖),因此没有问题。

4.3.发生循环依赖的且BeanA在属性赋值和初始化的过程更换对象

image.png BeanB引用了错误的对象A,Spring报循环依赖的错误。

五、Spring中的三级缓存

5.1.三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
}

singletonObjects:缓存创建完成的bean

earlySingletonObjects:缓存仅仅做了实例化的bean,且被其他的bean依赖了的bean,即缓存了循环依赖的bean。

singletonFactories:缓存获取earlySingletonObjects工厂,实际上的意思是,缓存了缓存仅仅做了实例化的bean,但是没有被其他的bean依赖bean

earlySingletonObjects和singletonFactories两者都是缓存实例化阶段之后的bean,区别在于前置实例化完后,有其他的bean依赖了它,后者暂时还没有其他的bean依赖它

5.2.三级缓存中对象的获取

image.png

Spring需要一个bean(单例)时,或尝试先获取,如果没有,才会去创建,代码如下:

Xnip2021-07-06_20-00-14.jpg 红框1是判断这个bean是否已经创建,如果已经创建就直接返回

红框2是创建bean的逻辑,即bean的生命周期那部分代码

我们来详细看看红框1的代码:

image.png

image-20210705213055055.png

这部分代码的逻辑就是singletonObjects有就返回singletonObjects里面的,没有的就返回earlySingletonObjects里面的,没有就用singletonFactories获得一个earlySingletonObject,放到earlySingletonObjects里面去,同时移除singletonFactories里面的singletonFactory

所以Sping获取一个bean的顺序就是singletonObjects > earlySingletonObjects > singletonFactories#getObject > createBean

5.3.三级缓存中对象的添加与删除

5.3.1.singletonFactory的添加时机。

当bean实例化完后,添加singletonFactory的缓存 singletonFactory里面实际上存放的是实例化之后的bean,其添加到缓存中的时机在bean初始化结束的时候

image-20210705211730705.png image-20210705211911773.png

从代码中,我们可以看到在bean实例化之后就会创建一个ObjectFactory放到singletonFactories中,ObjectFactory里面有一个方法getObject,getObject调用getEarlyBeanReference,getEarlyBeanReference实际上就是将实例化完的bean给返回

5.3.2.singletonFactory的删除时机。

当成为earlySingletonObject或singletonObject时删除。

5.3.3.earlySingletonObject的添加时机。

当发生循环依赖时,即获取完singletonFactory后删除singletonFactory添加earlySingletonObject。(如上面的流程图所示)

5.3.4.earlySingletonObject删除时机。

当bean创建完毕时删除

5.3.5.singletonObject添加时机。

当bean创建完毕时,删除earlySingletonObject,添加singletonObject