文章目錄
- 前言
- 一、循環依賴問題
- 二、循環依賴的解決
- 三、整體流程分析
前言
??常見的可能存在循環依賴的情況如下:
- 兩個bean中互相持有對方作為自己的屬性。
??類似于:
- 兩個bean中互相持有對方作為自己的屬性,且在構造時就需要傳入:
??類似于:
- 在某個bean中注入自身:
??其中第二種構造方法的循環依賴一般情況下是無解的,除非加上@Lazy
注解。本篇重點分析第一種循環依賴Spring是如何解決的。
一、循環依賴問題
??Spring在創建一個bean時,簡單來說會經過實例化bean,屬性注入,初始化的操作。當出現第一種循環依賴時,可能會經歷以下的過程:
??AService
- 去單例池中找有無AService實例,此時沒有,執行
doCreateBean
。 createBeanInstance
創建出AService實例。- 執行AService實例的屬性填充。
- AService的初始化、初始化前。
- AService的初始化后。
- 放入單例池。
??其中在執行AService實例的屬性填充
這一步,會根據@AutoWired
的注入點,去尋找BService實例:
- 去單例池中找有無BService實例,此時沒有,執行
doCreateBean
。 - createBeanInstance`創建出BService實例。
- 執行BService實例的屬性填充。
- BService的初始化、初始化前。
- BService的初始化后。
- 放入單例池。
??其中在執行BService實例的屬性填充
這一步,會根據@AutoWired
的注入點,發現需要填充AService實例,這就出現了循環依賴的問題。
二、循環依賴的解決
??那么Spring是如何解決循環依賴的?主要是通過三級緩存
的機制去實現的:
singletonObjects :一級緩存 earlySingletonObjects 二級緩存 singletonFactories 三級緩存
??可以看到,首先在doCreateBean
的方法中,bean實例化后,屬性填充之前,先有一段圖上的邏輯:
- 判斷當前的bean是否是單例的,以及是否支持循環依賴(默認是true),以及
singletonsCurrentlyInCreation
集合中是否包含當前bean。 - 如果滿足條件,就把當前的bean的名稱,和一段lambda表達式,放入
singletonFactories
集合中。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {//單例池中沒有該beanif (!this.singletonObjects.containsKey(beanName)) {//向單例工廠緩存當前bean名稱以及對應的lambda表達式this.singletonFactories.put(beanName, singletonFactory);//從早期單例對象的緩存中去除當前的beanthis.earlySingletonObjects.remove(beanName);//向已注冊的單例bean集合中添加當前bean名稱this.registeredSingletons.add(beanName);}}}
??這里的lambda表達式,主要是為了返回一個可以被外部提前訪問的 bean 實例。注意:lambda表達式不是在此處執行,而是先放入了
earlySingletonObjects
集合中!
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {//如果 Spring AOP 代理在這里介入,那么 getEarlyBeanReference() 可能會返回一個動態代理對象,而不是原始 beanexposedObject = bp.getEarlyBeanReference(exposedObject, beanName);}}return exposedObject;}
返回可能經過 AOP 代理的 bean
判斷是否需要AOP
??并且在執行doGetBean
時,會首先執行getSingleton
方法:
??在getSingleton
方法中,運用了雙檢鎖模式,避免在加鎖后其它線程已經創建并緩存了該 bean。并且上面提到的lambda表達式,會在此處真正地去執行
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 嘗試從單例池中獲取當前 bean 的實例,避免加鎖操作,提高性能Object singletonObject = this.singletonObjects.get(beanName);// 如果單例池(一級緩存)中沒有找到,并且當前 bean 正在創建過程中(循環依賴的處理)if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 嘗試從早期單例池(二級緩存)中獲取當前 bean(即尚未完全初始化的對象)singletonObject = this.earlySingletonObjects.get(beanName);// 如果在早期單例池中也沒有找到,并且允許獲取早期引用(通常是為了解決循環依賴)if (singletonObject == null && allowEarlyReference) {// 加鎖,避免并發創建同一個 bean 導致不一致的問題synchronized (this.singletonObjects) {// 再次檢查,避免在加鎖后其它線程已經創建并緩存了該 beansingletonObject = this.singletonObjects.get(beanName);// 如果單例池中依然沒有找到該 beanif (singletonObject == null) {// 嘗試從早期單例池中獲取,如果仍然沒有找到singletonObject = this.earlySingletonObjects.get(beanName);// 如果早期單例池中也沒有找到,嘗試從工廠中獲取 bean(即懶加載)if (singletonObject == null) {// 獲取 bean 的工廠方法(三級緩存)ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);// 如果工廠方法存在,說明該 bean 尚未初始化,且支持懶加載if (singletonFactory != null) {// 使用工廠創建 bean 并緩存到早期單例池中,防止循環依賴singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject); // 緩存早期對象this.singletonFactories.remove(beanName); // 移除工廠方法,因為 bean 已經創建}}}}}}// 返回獲取到的 singletonObject,如果沒有找到,則返回 nullreturn singletonObject;
}
??這里的isSingletonCurrentlyInCreation
方法,是判斷當前的bean名稱是否在singletonsCurrentlyInCreation
集合中,那么bean是在什么時候存入該集合的呢?答案是在下方重載的getSingleton
方法中:
無論if條件是否成立,都會把當前的bean名稱放入singletonsCurrentlyInCreation集合中
??而在執行完createBean(包括實例化,依賴注入,初始化)之后,會執行addSingleton
方法:
??會將當前bean從二級、三級緩存中移除,并放入單例池中,表示該bean已經完全創建完成。
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {//將當前初始化完成的bean存入單例池(一級緩存)this.singletonObjects.put(beanName, singletonObject);//從三級緩存中刪除當前beanthis.singletonFactories.remove(beanName);//從二級緩存中刪除當前beanthis.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}
三、整體流程分析
A和B的創建流程 藍色代表A 綠色代表B
??A嘗試從單例池中獲取
??條件不滿足,返回null
??進入
getSingleton
??進入getSingleton
,執行beforeSingletonCreation
,將A放入singletonsCurrentlyInCreation
中,代表A正在被創建。
??進入
doCreateBean
的addSingletonFactory
方法:
??進入
doCreateBean
的addSingletonFactory
方法,將A放入三級緩存中
??執行A的屬性填充(A有一個屬性為B):
??嘗試從容器中獲取B
??這里的條件依舊不滿足,返回null:
??進入
getSingleton
,執行beforeSingletonCreation
,同樣將B放入singletonsCurrentlyInCreation
中,代表B正在被創建。
??進入
doCreateBean
的addSingletonFactory
??同樣將B放入三級緩存
??此時的緩存情況:A和B只在二級緩存中
??進行B的屬性填充**(B中有一個A屬性)**
??再次嘗試從容器中獲取A
??此時的條件滿足:
??從三級緩存中取出A的lambda表達式執行:
??此時的A的B屬性是沒有值的
??放入二級緩存,并從三級緩存中刪除:
??此時的緩存情況:
??直接返回,不走createBean的邏輯了。
??給B的A屬性賦值,完成屬性填充:
??B繼續執行初始化
??執行B的
getSingleton
的addSingleton
方法,將B放入單例池,并且清除二三級緩存:
??回到A的屬性填充,填充了B屬性
??填充完成后,A的B屬性有值了,B的A屬性也有值了,繼續執行A的初始化,完成后將A放入單例池,并且清除二三級緩存:
??此時的二三級緩存全部清空:
??至此整個流程全部結束。
??為什么要加入三級緩存?因為上面的過程只是普通情況,還需要考慮到AOP的情況。如果開啟了AOP,那么會在初始化后,**基于切面生成一個代理對象。**而循環依賴
的觸發時機是在屬性注入時,如果只使用普通的緩存,會導致解決循環依賴時注入的對象是普通對象,而最終的對象是代理對象,產生不一致的情況。
??applyBeanPostProcessorsAfterInitialization
是初始化后執行的方法,也是在AOP的場景下生成代理對象的方法:
??如果開啟了AOP,那么在循環中會執行
AbstractAutoProxyCreator
的postProcessAfterInitialization
方法生成代理,在這一步中會判斷當前Bean是否已經在解決了循環依賴的過程中進行了AOP,如果已經進行過了,就不會再次生成代理,保證代理對象的唯一性。