什麼是Spring中的迴圈依賴?
迴圈依賴是指兩個或者多個bean互相依賴對方,從而形成一個閉環。例如:Bean A依賴於Bean B,而Bean B又依賴於Bean A。可能會導致Spring在嘗試建立這些bean例項時出現問題,因為他們互相等待對方被建立,最終導致應用程式無法啟動。
Spring是如何發現這種迴圈依賴的問題的呢?
透過依賴圖來檢測和發現迴圈依賴問題。如下步驟:
1 Bean的建立過程
Spring容器在啟動時,會掃描配置檔案(appliactionContext.xml)或者註解定義的bean,並且嘗試建立這些bean的例項。建立bean例項的過程如下
- 例項化:建立bean的例項。
- 屬性填充:為bean注入依賴其他的bean。
- 初始化:執行自定義的初始化方法。
2 依賴注入過程
在屬性填充階段,Spring會為每個bean注入他所依賴的bean。在這個過程中,Spring會跟蹤哪些bean正在被建立,以便檢測迴圈依賴。
3 迴圈依賴檢測機制
Spring透過一個名為“DefaultSingletonBeanRegistry”的類來跟蹤單例bean的建立狀態。該類維護了三個主要的快取來管理bean的建立過程。
- singletonObjects:一級快取(儲存完全初始化好的bean)
- earlySingletonObjects:二級快取(儲存早期暴露的單例bean,用於解決迴圈依賴)
- singletonFactories:三級快取(儲存用於建立bean例項的工廠)
具體的檢測步驟
例項化階段
當Spring開始例項化一個bean時,它會將這個bean標記為正在建立。這一步是透過將bean名稱新增到一個“正在建立中的bean”集合(‘singletonCurrentlyInCreaontion’)中來實現的。
屬性填充階段
在屬性填充階段,Sping會為該bean注入其依賴的其他的bean。此時Spring會檢查這個“其他的bean”是否已經在建立過程中。
- 如果依賴的bean已經在建立中,Spring會檢測到迴圈依賴,並根據不同的注入方式採取不同的處理方式。
- 如果是建構函式注入,Spring會丟擲‘BeanCurrentlyInCreationException’,因為無法解決建構函式中注入的迴圈依賴。
- 如果是Setter注入,Spring會從‘earlySingletonObjects’或‘singletonFactories’中獲取依賴的bean,提前暴露一個部分建立的bean引用來解決迴圈依賴。
舉例
如Bean A和Bean B迴圈依賴
@Component
public class A {
@Autowired
private B b;
public A() {
System.out.println("A is created");
}
}
@Component
public class B {
@Autowired
private A a;
public B() {
System.out.println("B is created");
}
}
Spring的依賴注入過程:
1 例項化Bean A
- 將Bean A 標記為正在建立,並新增到‘singletonCurrentlyInCreation’集合中。
- 例項化Bean A,並將其放入到三級快取‘singletonFactories’中。(三級快取存放的是建立例項化的bean工廠)
2 填充Bean A的屬性
- 此時發現Bean A依賴Bean B ,於是開始建立Bean B。
- 將Bean B標記為正在建立,並放入到‘singletonCurrentlyInCreation’集合中。
- 例項化Bean B,並將其放入到三級快取‘singletonFactories’中。
3 填充Bean B的屬性
- 發現Bean B依賴於Bean A 。此時,檢查到Bean A 已經在建立過程中,因此發現了迴圈依賴。
- 由於是Setter方法注入,Spring會從三級快取‘singletonFactories’中獲取一個部分建立的Bean A例項,提前暴露出來,放入二級快取‘earlySingletonObjects’中。
- 使用這個部分建立出來的Bean A例項 來填充Bean B的屬性。
4 完成Bean B的建立
- Bean B中的所有屬性填充完畢後,Spring將Bean B的例項從三級快取‘singletonFactories’中移到一級快取‘singletonObjects’完全初始化好的bean中。
5 回到Bean A的建立
- 繼續為Bean A填充屬性,此時可以從一級快取‘singletonObejcts’中獲取到完整的Bean B例項。
- 完成Bean A的建立,並將其從三級快取‘singletonFactories’中移到一級快取‘singletonObjects’中。