Spring循環依賴的解決方法詳解

說明:spring如何解決循環依賴,是面試中經常問到的題目,今天我們就來分享一下spring是如何解決循環依賴問題的。

什麼是循環依賴:

我們先來看看官方文檔的說法:

通俗來講,就是A依賴B或者B依賴A,或者C依賴自己本身,或是三個以上,例如A依賴B,B依賴C,C又依賴A。如下圖:

Spring實例Bean的本質

Spring在實例化一個bean的時候,是首先遞歸的實例化其所依賴的所有bean,直到某個bean沒有依賴其他bean,此時就會將該實例返回,然後反遞歸的將獲取到的bean設置為各個上層bean的屬性的。

循環依賴主要場景

什麼情況下循環依賴可以被解決

Spring解決循環依賴是有前置條件的

  • 出現循環依賴的Bean必須要是單例(singleton),如果依賴prototype則完全不會有此需求。
  • 依賴註入的方式不能全是構造器註入的方式。

解決方式

Spring是通過三級緩存來解決上述問題的:

一級緩存: singletonObjects存儲的是所有創建好瞭的單例Bean

二級緩存:earlySingletonObjects完成實例化,但是還未進行屬性註入及初始化的對象

三級緩存:singletonFactories提前暴露的一個單例工廠,二級緩存中存儲的就是從這個工廠中獲取到的對象。

三級緩存解決循環依賴流程:

  1. 獲取A時首先會嘗試從一級緩存singletonObjects中獲取;
  2. 獲取不到就再從二級緩存earlySingletonObjects中獲取;
  3. 若是還沒有則嘗試從三級緩存singletonFactories獲取;
  4. 還是沒有獲取到則再嘗試創建A對象
  5. 會執行doGetBean->createBean->createBeanInstance並使用構造器實例化
  6. 在嘗試給A進行初始化時,由於B不存在無法完成初始化,則將半成品A放入第二級緩存中,進入B的創建流程。
  7. 與先前過程相似,在第三級緩存中放入beanName和表達式sharedInstance,進入B的初始化過程
  8. 由於在第二級緩存中可以找到A,則B可以完成初始化,將成品Bean放入一級緩存中備用,刪除三級緩存中的B
  9. 同時完成A的初始化,並刪除二級緩存中的半成品A

具體流程圖如下:

最後我們來個小小的總結:

Spring通過三級緩存解決瞭循環依賴,其中一級緩存為單例池(singletonObjects),二級緩存為早期曝光對象earlySingletonObjects,三級緩存為早期曝光對象工廠(singletonFactories)。

當A、B兩個類發生循環引用時,在A完成實例化後,就使用實例化後的對象去創建一個對象工廠,添加到三級緩存中,如果A被AOP代理,那麼通過這個工廠獲取到的就是A代理後的對象,如果A沒有被AOP代理,那麼這個工廠獲取到的就是A實例化的對象。

當A進行屬性註入時,會去創建B,同時B又依賴瞭A,所以創建B的同時又會去調用getBean(a)來獲取需要的依賴,此時的getBean(a)會從緩存中獲取。

  • 先獲取到三級緩存中的工廠;
  • 調用對象工工廠的getObject方法來獲取到對應的對象,得到這個對象後將其註入到B中。緊接著B會走完它的生命周期流程,包括初始化、後置處理器等。
  • 當B創建完後,會將B再註入到A中,此時A再完成它的整個生命周期。至此,循環依賴結束!

到此這篇關於Spring循環依賴的解決方法詳解的文章就介紹到這瞭,更多相關Spring循環依賴內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: