【Spring】快速理解迴圈依賴

活在夢裡丶發表於2020-12-16

什麼是迴圈依賴?

迴圈依賴簡單來說就是A物件中依賴了B物件,B物件中依賴了A物件。Java中解決迴圈依賴也很簡單,直接set就好了。

 

spring當中的迴圈依賴和Java當中的迴圈依賴又有什麼區別呢?

主要區別在於,spring當中的一個物件並不是簡單的new出來的,而是經過了一系列複雜的Bean的生命週期而產生的,就是因為Bean的生命週期所以才會產生迴圈依賴問題。所以要明白Spring中的迴圈依賴,需要先明白Spring中Bean的生命週期。

Bean的生命週期

被Spring所管理的物件叫做Bean,Spring生成一個Bean的步驟如下:

  1. 掃描指定路徑下的Class,封裝成BeanDefinition
  2. 根據BeanDefinition推斷Class構造方法,反射得到一個原始物件
  3. 對原始物件中的屬性進行填充(依賴注入)
  4. 如果原始物件中某個方法有切面,那麼則根據原始物件生成一個代理物件
  5. 把最終生產的代理物件放入單例池(singletonObject)中,下次getBean時直接從單例池獲取

Spring生產Bean的過程肯定遠比上面的多,我們可以發現,Spring中構造一個原始物件後就需要給屬性注入了,這個注入過程是什麼樣的呢?

比如上文中的A類,首先生成一個A類的原始物件,然後就會對A類進行注入;注入的時候發現,A類存在B型別b屬性,此時Spring會從BeanFactory中獲取B型別對應的單例bean。如果不存在那麼就回去生成B對應的Bean。

而生成B的時候,發現B中存在一個A型別的屬性a,但是此時a還沒有建立完成,又會觸發a的生命週期。所以這裡就產生了迴圈依賴。

Spring如何解決的迴圈依賴?

解決迴圈依賴最重要的就是打破這個迴圈,當A物件還在建立的時候把物件提前放入快取;當B需要注入a的時候,從快取中拿到A提前暴露的物件。

屬性注入肯定是在生成原始物件之後,我們能暴露的就是A的原始物件,但是如果A中的方法有切面,就會經過AOP後生成了新的代理物件。那麼此時單例池中的A是代理物件,而B依賴的a卻是原始物件。

所以spring引入了三級快取,不直接儲存物件的原始型別,而是存著某個beanName對應的ObjectFactory,在Bean的生命週期中,生成完原始物件後,就會構造一個ObjectFactory存入三級快取,ObjectFactory是一個函式式介面,呼叫該介面就會提前進行AOP生成一個代理。

所以當B注入a將要發生迴圈依賴的時候,首先進行a的AOP生成代理物件注入給B中的a。當B建立完成了之後,A繼續進行生命週期,而A完成屬性注入後,會按照本身的邏輯進行AOP,因為A已經進行了AOP會跳過次步驟。

此時的A還是一個原始物件,所以最後會判斷一個二級快取中有沒有A的代理,如果發現存在就會進行物件替換,將代理物件A放入單例池。

相關文章