1. 程式人生 > >SpringIOC迴圈依賴

SpringIOC迴圈依賴

[Toc] ## 1. 什麼是迴圈依賴 迴圈依賴其實就是迴圈引⽤,也就是兩個或者兩個以上的 Bean 互相持有對⽅,最終形成閉環。⽐如A依賴於B,B依賴於C,C⼜依賴於A ![image-20201201152029434](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201201152029434.png) #### 注意: #### 這⾥不是函式的迴圈調⽤,是物件的相互依賴關係。 #### 迴圈調⽤其實就是⼀個死迴圈,除⾮有終結 條件。 **Spring中迴圈依賴場景有:** - 構造器的迴圈依賴(構造器注⼊) - Field 屬性的迴圈依賴(set注⼊) 其中,構造器的迴圈依賴問題⽆法解決,只能丟擲 BeanCurrentlyInCreationException 異常,**在解決屬性迴圈依賴時,spring採⽤的是提前暴露物件的⽅法。** --- ## 2. 迴圈依賴處理機制 - 單例 bean 構造器引數迴圈依賴(⽆法解決) - prototype 原型 bean迴圈依賴(⽆法解決) 因為prototype 原型 bean ,產生物件之後是不在容器中管理的。 - **單例bean通過setXxx或者@Autowired進行迴圈依賴(可以解決)** ### 2.1 演示場景: ``` java //lagouBen 依賴於 ItBean public class LagouBean { private ItBean itBean; public void setItBean(ItBean itBean) { this.itBean = itBean; } public LagouBean() { System.out.println("LagouBean 構造器"); } } ``` ``` java //ItBean 依賴於 LagouBen public class ItBean { private LagouBean lagouBean; public void setLagouBean(LagouBean lagouBean) { this.lagouBean = lagouBean; } public ItBean() { System.out.println("ItBean...構造器"); } } ``` ``` xml ``` ### 2.2 處理機制簡圖 ![image-20201201152103646](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201201152103646.png) ![image-20201201152127499](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201201152127499.png) ### 總結: A依賴於B ,B 依賴於A A在建立過程中 : 1. 首先會建立Bean例項(**僅僅呼叫構造方法,但是尚未設定屬性,通過反射完成物件的初始化**), 2. 然後判斷是否是單例,是否有迴圈依賴。 3. 把建立好的Bean例項放入三級快取——singletonFactories ![image-20201125223907958](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201125223907958.png) 4. 然後將要給A Bean裝配屬性,發現依賴B ![image-20201125225107552](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201125225107552.png) 5. 呼叫deGetBean() 想拿到B,首先從**一級快取**(**singletonObjects**)中拿,然後從**二級快取中**(**earlySingletonObjects**)拿,然後從**三級快取**(**singletonFactories**)拿,都拿不到,那就開始建立B Bean ![image-20201125231459191](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201125231459191.png) 6. 把建立好的B Bean例項放入三級快取——singletonFactories,發現依賴於A 7. 呼叫deGetBean() 想拿到A,首先從**一級快取**(**singletonObjects**)中拿,然後從**二級快取中**(**earlySingletonObjects**)拿,都沒拿到。然後從三級快取中拿,**拿到了** ![image-20201125232711963](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201125232711963.png) 8. 拿到A Bean之後如上圖,放到**二級快取(earlySingletonObjects)**中,然後從**三級快取(singletonFactories)**中刪除。然後給B bean賦值了。 9. 此時B Bean 就裝配好了 放入一級快取池中。 10. B 裝配好了之後,A 就能順利的裝配了,然後呼叫`addSingleton()`方法,把A 從二級三級快取中刪除,然後**放到一級快取也就是單例池中**。 ![image-20201125234002342](https://typora-files.oss-cn-beijing.aliyuncs.com/file/image-20201125234002342.png) 11. 完成 **注意:** 這個案例中,B不會放到二級快取,只有在B依賴的一個物件尚未例項化的時候才會把B放到二級快取。例如: A依賴B,B依賴A和C,C依賴B。 先建立A,把尚未賦值的A放到三級快取,然後賦值B,找不到B,然後建立B,然後把尚未賦值的B放到三級快取,然後在建立B的過程中從三級快取找A(同時把A從三級快取中刪除然後加入到二級快取),然後B還有個屬性C,賦值C,從快取中找不到C,然後建立C,然後把尚未賦值的B放到三級快取,建立C的過程中發現C依賴於B,**然後可以從三級快取中找到B,然後把B放到二級快取**,C就裝配完畢了,放到一級快取。同時B也有了A和C,B裝配完畢了,放到一級快取。 A依賴B,B已經OK了,那麼A也裝配完畢了。 ```java /** Cache of singleton factories: bean name to ObjectFactory. */ //三級快取 private final Map> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ //二級快取 private final Map earlySingletonObjects = new ConcurrentHashMap<>(16); /** Cache of singleton objects: bean name to bean instance. */ //一級快取 private final Map singletonObjects = new ConcurrentHashMap<>(25