最通俗的方式理解Spring循環依賴三級緩存
今天,有位粉絲找我,說要耽誤我5分鐘時間,想讓我幫助它理解一下Spring循環依賴的三級緩存,繞暈了一個星期,沒有想明白。我想今天,用最通俗易懂的方式給大家重新梳理一下,保證讓你聽懂了。
1、什么是循環依賴?
循環依賴就是指循環引用,是兩個或多個Bean相互之間的持有對方的引用。循環依賴有三種形態:
(1)相互依賴,也就是A 依賴 B,B 又依賴 A,它們之間形成了循環依賴。
(2)三者間依賴,也就是A 依賴 B,B 依賴 C,C 又依賴 A,形成了循環依賴。
(3)自我依賴,也是A依賴A形成了循環依賴自己依賴自己。
2、如何解決循環依賴問題?
循環依賴本身沒有問題,問題是Spring中加入了依賴注入機制,也就是自動給屬性賦值。當創建Bean實例化以后,需要給Bean中需要賦值的屬性全部自動賦值才能交給用戶使用。但如果是循環依賴的情況,以兩個Bean相互依賴的情況為例,
假設Bean A已經實例化,但是Bean A中需要自動賦值Bean B并沒有初始化,但如果Spring立刻去初始化Bean B,發現Bean B中需要自動賦值的Bean A沒有初始化,如果這樣相互等待,就會形成死循環,最終,有可能導致Spring容器都無法啟動。
就好比,我們以前讀書的時候,老師經常教我們一個考試方法,就是遇到難題不會答的時候,不要死磕,要繼續往下做其他的題。否則,會因為一道難題卡住影響到整個的答題進度,還會影響正常的發揮,影響考試結果。
那這個問題該怎么解決呢?使用緩存。
就是將所有實例化好的Bean,全部放到一個容器中緩存起來,并且將已經完成實例化但沒有完成賦值的,打上標記。
然后,等Bean全部實例化以后,再重新掃描一遍容器,將沒有完成賦值的Bean屬性完成賦值,這個時候,所有未完成賦值的Bean都已經能夠找到對應的實例了。
那么問題來了。解決循環依賴問題,一定要二級緩存嗎?答案是不一定。但是Spring中為什么又要設計二級緩存呢?
這時候,我們可以這樣理解,假設,我們只有一個緩存容器,并且緩存是直接開放給用戶可以調用的,如果將未完成賦值的Bean和已完成賦值的Bean全部放到同一個容器,那這個時候,調用者就有可能拿到未賦值的Bean,這樣的Bean對于用戶來說是不可用的,可能會導致空指針異常。
所以,Spring設計者,才有了這樣一個設計,將能夠直接提供給用戶使用的Bean放到一級緩存中,這樣Bean稱之為終態Bean,或者叫成熟Bean。
將已經完成初始化,但還不能提供給用戶使用的Bean單獨放到一個緩存容器中,就是二級緩存,這樣的Bean稱之為臨時Bean,或者叫早期Bean。
依照以上的分析,理論上二級緩存就能解決循環依賴問題,那為什么Spring還要設計一個三級緩存呢?
3、如何理解三級緩存?
我們都知道,Spring中有很多注入的Bean是需要創建代理Bean的,但是,不是所有的Bean都需要再實例化之后立馬就會創建代理Bean。是要等到Bean初始化全部完成之后才創建代理Bean。因此,循環依賴的出現,Spring又不得不去提前創建代理Bean。如果不創建代理Bean,注入原始Bean就會產生錯誤。因 此,Spring設計三級緩存,專門用來存放代理Bean。但是,創建代理Bean的又不同的規則,因此,Spring三級緩存中,并不是直接保存代理Bean的引用,而是保存創建代理Bean的Factory。
4、總結
所以,總結結論為,單純解決循環依賴可以只用二級緩存,但是如果涉及到代理對象的循環依賴,就需要用到三級緩存。其實一、二、三級緩存是根據獲取對象的順序來命名的,我們完全可以這樣理解,一級緩存就是終態緩存,二級緩存是臨時緩存、三級緩存是代理工廠的緩存。
這張圖完整地描述了一、二、三級緩存的運行邏輯。