Spring迴圈依賴+案例解析

有点儿意思發表於2024-07-31

什麼是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’中。

相關文章