likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 14

“ 本文正在参加「金石计划」 ”

1:为什么会产生循环依赖

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:此时就造成了循环依赖的情况

2:Spring是如何解决循环依赖的

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

  • singletonObjects:缓存已经初始化好的Bean
  • earlySingletonObjects:缓存正在初始化的Bean
  • singletonFactories:缓存的创建Bean的ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。

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

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

  • 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代理过后的代理对象呢?

4: 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的一次代理,那么作为单例来说,这二个代理对象是不一样的。

5:第四级别缓存 - earlyProxyReferences

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

6:总结

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