天天看點

Spring為什麼需要三級緩存來解決循環依賴

作者:馬士兵教育CTO
Spring為什麼需要三級緩存來解決循環依賴

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這一級的緩存,是以至少是三級緩存才能解決循環依賴