《系列二》-- 5、單例bean緩存的獲取

閱讀之前要注意的東西:本文就是主打流水賬式的源碼閱讀,主導的是一個參考,主要內容需要看官自己去源碼中驗證。全系列文章基於 spring 源碼 5.x 版本。

寫在開始前的話:

閱讀spring 源碼實在是一件龐大的工作,不說全部內容,單就最基本核心部分包含的東西就需要很長時間去消化了:

  • beans
  • core
  • context

實際上我在博客裏貼出來的還只是一部分內容,更多的內容,我放在了個人,fork自 spring 官方源碼倉了; 而且對源碼的學習,必須是要跟着實際代碼層層遞進的,不然只是乾巴巴的文字味同嚼蠟。

https://gitee.com/bokerr/spring-framework-5.0.x-study

這個倉設置的公共倉,可以直接拉取。



Spring源碼閱讀系列--全局目錄.md



回到 doGetBean 初始的位置:

img.png

img.png

1 判斷bean是否完成整個加載流程

    /** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

如下是 singletonObjects 的定義,一個線程安全的HashMap,顧名思義只有是單例的bean被成功加載後纔會被它緩存,所以非單例bean 是不可能從中獲取到的。

如果我們請求的bean 是單例bean且已經被加載過,完成了整個加載流程,此時程序在方法第一行就已經可以退出了。

不是就接着往下:

2 判斷當前bean是否被加載過,是否已作爲提前暴露的bean

這裏的邏輯其實很簡單,無非就是雙重鎖機制。第一次獲取到null後,判斷當前bean是否已經進入創建流程 isSingletonCurrentlyInCreation 這個方法名很直觀。

isSingletonCurrentlyInCreation 返回true時,說明該bean已經在別的線程 (或遞歸循環依賴) 中進入創建流程了。

【PS * 前邊提到過,bean被加載時,都會遞歸的去加載它所依賴的 bean】

這裏還有另一位主角:

    /** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

它用於提前暴露bean, 你看這裏先取了一次,沒取到才正式進入創建流程,同樣也是爲了避免提前暴露動作重複。

關於循環依賴

還有另一位需要被重視的成員:

    /** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

看到了麼,它保存的對象實現了 ObjectFactory 接口,不陌生吧,前邊講循環依賴消解的文章介紹過它。

這裏還有另一個很有意思的現象不知道你有沒有注意到:earlySingletonObjects保存該bean之後,就立即從 singletonFactories 中移除了。

這裏說明他們極有可能是一個傳遞關係:當一個bean 第一次被加載時,默認會保存到 singletonFactories,
這時候這個bean 還在創建流程中,可能出現的情況是這時有另一個 bean 也依賴於它 (參考循環依賴,如果 spring 加載bean的過程是單線程,這時鐵定出現循環依賴了。)

根據前邊講循環依賴的文章:

this.earlySingletonObjects.put(beanName, singletonObject);

bean的提前暴露它不就來了麼?別看這裏只有幾行代碼,結合上下文之後纔會知道它的重要性。

img.png

前邊講到 bean 從 singletonFactories 傳遞到 earlySingletonObjects

那麼我們再去追究下, singletonFactories 中是什麼時候注入的,然後發現了只有一個地方調用了其上的:singletonFactories.put() 方法

再往上追蹤,我們發現了一個名叫: doCreateBean 的方法,這是我們後續流程中關注的方法;它處於從 0 開始創建一個bean 的流程中。

先記住它,後邊我們會再見到它的。

結合上邊分析的流程,這裏還有另一個定義,那就是三級緩存;spring 在消解循環依賴時引入了三級緩存:

  • 一級緩存: Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256)

  • 二級緩存: Map<String, Object> earlySingletonObjects = new HashMap<>(16)

  • 三級緩存: Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16)

首先時創建全新bean 時,將其注入了 三級緩存中。

所以創建bean 的流程時,先從一級緩存獲取,如果成功直接返回;

否則從二級緩存中,成功獲取到bean,那麼直接返回;

如果從二級緩存也無法獲取,那麼嘗試從三級緩存獲取,若從三級緩存成功獲取,那麼從三級緩存中移除該bean,並轉移到二級緩存中。

最終,若三級緩存也無法獲取,說明是在獲取一個從未被加載的 bean。

最終,第一次被加載的bean,最初會被緩存到,三級緩存中,bean 創建流程中,關注上述提到的 doCreateBean()方法即可閉環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章