什么是三級快取
在 DefaultSingletonBeanRegistry 類(都說Spring是個大的bean工廠,那這個類就是工廠的大倉庫,存放各種生產好,或生產中的bean)中有下面三個屬性

為什么需要三級快取
在這里我們需要一些前置的知識————Spring創建bean的流程中4個最基本的方法
getBean--------從bean工廠獲取bean
createBeanInstance--------創建bean實體,其實就是通過反射生成bean
populateBean--------填充bean的屬性(因為反射生成的bean就是個空殼子,所有屬性都為空,需要在這里填充)
initializeBean--------執行bean的初始換方法,以及通過bean的后置處理器對bean進行增強(這里我們關心的是bean后置處理對bean做了怎樣的增強)
假設只有一級快取,但兩個Bean之間相互參考
我提前寫好A和B兩個類

程式啟動,Spring會呼叫getBean方法獲取型別為A的bean,發現倉庫沒有,就會呼叫createBeanInstance方法通過反射呼叫A類的空參構造器創建A物件,但這時的A物件的屬性值都為空,

既然A物件屬性為空那么我們當然要填充bean,接下來就是呼叫populateBean方法,這時候發現A類中有個B類的屬性,并且是@Autowired注解修飾,那我們就要看看bean倉庫里面有沒有B的bean

當然B的bean現在是沒有的,那么就要實體化,和 A 一樣,B也同樣進行createBeanInstance方法,而且 B 類也有個 A 類的屬性需要自動裝配,那B同樣要執行 populateBean 方法,
問題來了,B在進行填充屬性的時候,又去呼叫getBean方法從倉庫中獲取 A 的bean,但發現沒有,
我之前假設了,如果只有一級快取,那這不就成了死回圈了么?
所以需要二級快取,在A一開始被實體化時,在還沒填充bean之前可以將bean放入二級快取,這樣B在填充bean時就可以獲取到半成品的A這個bean,緊接著A就可以獲得到成品的B這個bean(注意,這里的步驟都是我們的假想,Spring不是這么做的,但原理相似,畢竟我們還有個三級快取要說)

小結:二級快取解決回圈依賴問題
如果bean要被動態代理
在此之前我們還得搞清楚一個問題,什么時候進行的動態代理?
A 和 B 兩個物件假設都要進行動態代理

請問這時,A這個bean里面參考的B這個bean應該是代理物件還是普通物件呢?
對,A 和 B 的bean都應該參考物件的代理物件,只有這樣,才能在呼叫它們方法的時候進行增強(代理物件可以在原本要執行的方法前后和例外處等進行一些別的操作,比如事務、日志、快取等),
OK,那我們梳理一下,如果A先被 getBean 呼叫,剛開始會去單例池獲取,那肯定是沒有啊,那么就自己去通過反射創建,這個之前說過,但目前的 A 是個半成品的bean,不能放入提前參考的二級快取,假如要將該bean進行代理呢!那能不能直接代理,再放入,實作上我個人認為可以,但沒這個必要,畢竟浪費空間,執行效率也會降低,
所以又回到問題開始的地方,什么時候生成動態代理?
其實是2個時機
- 在getBean時當發現當前bean被其他bean參考
- 在普通bean被初始化完成后
一個個來說,為什么這么做,還是以 A 和 B 舉例,當A 生成半成品bean時,發現自己要填充 B 這個bean,緊接著呼叫 getBean(“b”),B 也生成半成品bean,也要填充,此時getBean(“a”)時,A發現自己還沒填充完成,就被別人參考了,不能直接回傳普通bean,先得看看自己需不需要被代理,發現需要,則直接回傳代理物件,
當 B 獲得 A 的代理物件后,執行初始化方法,之前說過,初始化方法會做兩件事,
- 執行自定義的初始化代碼
- 執行bean的后置處理器方法
在執行bean的后置處理器方法時,如果發現當前bean要被代理,則回傳的是代理物件,最后 A 也就能獲得 B 的代理物件,依賴回圈和動態代理就此結束,
神馬?這就結束了?三級快取呢?
三級快取它來了
在寫三級快取之前,我們先思考一個問題,我們的spring是先存放一級快取還是二級還是三級?
想知道這個問題,我們先打個斷點



這里說一下,他們的put方法只被呼叫一次,然后我們來看看這些加了條件的斷點停在哪?

是它,和你想的一樣么?
那我們再看看它這里面存的 singletonFacotry 究竟是個啥

半成品bean說明一點,就是 A 已經經過了反射,但為什么不放入二級快取呢?還是之前的那個問題,在被其他bean參考時我們必須保證當前被參考的bean如果是需要被代理的,要先進行bean的替換,
是不是這樣的呢?下面我們放開斷點,調到下一個放入二級快取的斷點中

順便說一下,這是Spring執行的哪一步呢

至此,A的代理替換完成,那 B 的代理在哪呢?

執行代碼之前

執行代碼之后

總結
各級快取的作用
一級快取:存放單例
二級快取:存放提前參考
三級快取:存放單例工廠
代理物件生成時機
- 被其他bean參考時
- 實體化bean完成之后
三級快取解決什么問題
- 回圈依賴
- 動態代理bean的替換
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292485.html
標籤:java
