写点什么

Spring 为什么需要三级缓存来解决循环依赖

  • 2023-04-11
    湖南
  • 本文字数:1694 字

    阅读完需:约 6 分钟

一、为什么会产生循环依赖

public class A {    private B b;}
public class B { private A a;}
复制代码

在 Spring 中初始化一个 Bean 并不是简单的 new A()这么简单,需要经过属性注入以及各种后置处理器的处理。

  • 1:当我们初始化 A 的时候,在属性注入会发现需要注入一个对象 B

  • 2:此时 Spring 就会去 Bean 容器中找是不是有这个 B 对象,如果有就返回

  • 3:如果没有就会初始化 B,此时在对 B 进行属性注入的时候发现需要 A,又要去初始化 A

  • 4:此时就造成了循环依赖的情况

二、Spring 是如何解决循环依赖的

Spring 是使用三级缓存来解决循环依赖的,准确来说应该是四层

  • singletonObjects:缓存已经初始化好的 Bean

  • earlySingletonObjects:缓存正在初始化的 Bean

  • singletonFactories:缓存的创建 Bean 的 ObjectFactory,表示对象工厂,表示用来创建早期 bean 对象的工厂。

三、为什么需要三级缓存?二层不行吗?一层呢?

  1. 创建 A,将 A 这个原始对象放入缓存中

  2. 属性注入,需要 B 对象

  3. 创建 B,进行属性注入需要 A 对象,从缓存中拿到原始对象 A 进行属性注入

  4. B 创建完毕,然后 A 从容器中拿到 B 进行属性注入


为什么 B 可以使用缓存中的原始 A 对象,因为单例在容器中只有一个,虽然现在 B 中的属性 A 还是个原始对象,也就是还没有进行属性赋值,但是后续在 A 初始化完之后,B 使用的还是这个 A 对象


从图中可以看出其实我们使用一层缓存也可以解决循环依赖了,也就是 earlySingletonObjects,那 Spring 为什么还要整个三级缓存呢?


在我们创建 A 的使用,缓存中放的是原始 A 对象,如果这个对象是要经过 AOP 代理的对象怎么办呢?本来对象 B 中的 A 应该是个代理对象,但是现在缓存拿到的并不是代理对象,还有一个问题,对于 B 来说,我怎么知道要的这个 A 对象是普通对象还是需要经过 AOP 代理过后的代理对象呢?

四、singletonFactories 的作用

singletonFactories 中存的是某个 beanName 对应的 ObjectFactory,在 bean 的生命周期中,生成完原始对象之后,就会构造一个 ObjectFactory 存入 singletonFactories 中。这个 ObjectFactory 是一个函数式接口,所以支持 Lambda 表达式: () -> getEarlyBeanReference( beanName , mbd , bean )

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {	Object exposedObject = bean;	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {		for (BeanPostProcessor bp : getBeanPostProcessors()) {			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);			}		}	}	return exposedObject;}
复制代码

也就是说在初始化 A 的时候,在属性注入之前会提前创建 A 对象的 ObjectFactory 放到这个 singletonFactories 中,后续 B 对象在需要 A 对象的时候就会调用这个 () -> getEarlyBeanReference( beanName , mbd , bean ) 来获取 A 对象。此时如果这个 A 对象不需要经过 AOP 代理的就会生成一个普通对象,如果后续需要经过 AOP 代理此时就会生成一个代理对象返回给 B 进行属性注入。


所以真正解决循环依赖的是 singletonFactories,现在看起来是不是觉得没什么问题了呢


  • 假设这个 A 对象是需要经过 AOP 代理生成一个代理对象的,B 对象使用的也是 A 的代理对象,看起来似乎没什么问题,但是 AOP 代理是在初始化之后执行的,也就是说你现在虽然提前生成了 A 的代理对象,但是在初始化 A 对象之后还要经过 AOP 的一次代理,那么作为单例来说,这二个代理对象是不一样的。

五、第四级别缓存 - earlyProxyReferences

在经过 () -> getEarlyBeanReference(beanName,mbd,bean) 得到的对象如果是经过 AOP 代理的对象就会放到这个 earlyProxyReferences 中,这样后续在初始化后置处理器中只要判断 earlyProxyReferences 中有没有这个 Bean,如果有就不用再次做代理了

六、总结

Spring 在解决循环依赖虽然使用了三级缓存,可以说是四级缓存,但是真正解决循环依赖的主要还是 singletonFactories 这一级的缓存,所以至少是三级缓存才能解决循环依赖


作者:我是小趴菜

链接:https://juejin.cn/post/7219202318994309180

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
Spring为什么需要三级缓存来解决循环依赖_Java_做梦都在改BUG_InfoQ写作社区