spring源碼解析(五)-finishBeanFactoryInitialization

介紹:

前面三篇文章小編介紹了spring啓動過程中的三個重要方法:obtainFreshBeanFactory、invokeBeanFactoryPostProcessors、invokeBeanFactoryPostProcessors。。。。那麼還有一個更爲重要的方法,也是sping源碼的核心部分------普通bean的創建和初始化的過程。。。。。

 

概述:

該方法會實例化所有剩餘的非懶加載單例 bean。除了一些內部的 bean、實現了 BeanFactoryPostProcessor 接口的 bean、實現了 BeanPostProcessor 接口的 bean,其他的非懶加載單例 bean 都會在這個方法中被實例化,並且 BeanPostProcessor 的觸發也是在這個方法中。

 

 

前序:

在進入正文之前,我這裏先簡單介紹一個概念MergedBeanDefinition


在之後的內容你可能會頻繁的見到 MergedBeanDefinition,先介紹一下這個bean定義。

MergedBeanDefinition:這個詞其實不是一個官方詞,但是很接近,該詞主要是用來表示 “合併的 bean 定義”,因爲每次都寫 “合併的 bean 定義” 有點太繞口,因此我在之後的註釋或解析中或統一使用 MergedBeanDefinition 來表示 “合併的 bean 定義”。

之所以稱之爲 “合併的”,是因爲存在 “子定義” 和 “父定義” 的情況。對於一個 bean 定義來說,可能存在以下幾種情況:

1、 該 BeanDefinition 存在 “父定義”:首先使用 “父定義” 的參數構建一個 RootBeanDefinition,然後再使用該 BeanDefinition 的參數來進行覆蓋。
2 、該 BeanDefinition 不存在 “父定義”,並且該 BeanDefinition 的類型是 RootBeanDefinition:直接返回該 RootBeanDefinition 的一個克隆。
3、 該 BeanDefinition 不存在 “父定義”,但是該 BeanDefinition 的類型不是 RootBeanDefinition:使用該 BeanDefinition 的參數構建一個 RootBeanDefinition。
之所以區分出2和3,是因爲通常 BeanDefinition 在之前加載到 BeanFactory 中的時候,通常是被封裝成 GenericBeanDefinition ScannedGenericBeanDefinition,但是從這邊之後 bean 的後續流程處理都是針對 RootBeanDefinition,因此在這邊會統一將 BeanDefinition 轉換成 RootBeanDefinition

在我們日常使用的過程中,通常會是上面的第3種情況。如果我們使用 XML 配置來註冊 bean,則該 bean 定義會被封裝成:GenericBeanDefinition;如果我們使用註解的方式來註冊 bean,也就是<context:component-scan /> + @Compoment,則該 bean 定義會被封裝成 ScannedGenericBeanDefinition。
 


正文:我們找到finishBeanFactoryInitialization方法,,點擊進去:

finishBeanFactoryInitialization

實例化所有剩餘(非懶加載)單例對象,見代碼塊1詳解

代碼塊1:preInstantiateSingletons

3.獲取 beanName 對應的 MergedBeanDefinition,見代碼塊2詳解。

5.判斷 beanName 對應的 bean 是否爲 FactoryBean,見代碼塊6詳解

5.3 和 6 通過 beanName 獲取 bean 實例,是finishBeanFactoryInitialization 方法的核心,暫不做深入分析。。

7.遍歷 beanNames,觸發所有 SmartInitializingSingleton 的後初始化回調,這是 Spring 提供的一個擴展點,在所有非懶加載單例實例化結束後調用

代碼塊2:getMergedLocalBeanDefinition

3.2 根據 beanName 和 beanName 對應的 BeanDefinition,獲取 MergedBeanDefinition,見代碼塊3詳解

代碼塊3:getMergedBeanDefinition

 

 

 

5.1 獲取父定義的 beanName,這邊有一個 beanName 的轉換操作,之後會經常用到,見代碼塊4詳解。

5.3 獲取父定義的 MergedBeanDefinition,見代碼塊5詳解。

5.7 和 5.8 就是合併操作,也就是我們之前一直說的 MergedBeanDefinition 的由來。


8.將該 beanName 與 MergedBeanDefinition 放到 mergedBeanDefinitions 緩存,後續再走到代碼塊2時,就會直接返回緩存裏的數據。

 

代碼塊4:transformedBeanName

 

如上:將 name 真正解析成真正的 beanName,主要是去掉 FactoryBean 裏的 “&” 前綴,和解析別名。這邊簡單的介紹下 FactoryBean。

FactoryBean
一般情況下,Spring 通過反射機制利用 bean 的  class 屬性指定實現類來實例化 bean。而 FactoryBean 是一種特殊的 bean,它是個工廠 bean,可以自己創建 bean 實例,如果一個類實現了 FactoryBean 接口,則該類可以自己定義創建實例對象的方法,只需要實現它的 getObject() 方法

如下代碼:

 

 

代碼塊5:getMergedBeanDefinition

這邊引入了一個 “父 BeanFactory” 的概念

父BeanFactory:

在 Spring 中可能存在多個 BeanFactory,多個 BeanFactory 可能存在 “父工廠” 與 “子工廠” 的關係。最常見的例子就是:Spring MVC 的 BeanFactory 和 Spring 的 BeanFactory,通常情況下,Spring 的 BeanFactory 是 “父工廠”,Spring MVC 的 BeanFactory 是 “子工廠”,在 Spring 中,子工廠可以使用父工廠的 BeanDefinition,因而,如果在當前 BeanFactory 中找不到,而又存在父工廠,則會去父工廠中查找。

代碼塊6:isFactoryBean


 

2.嘗試從緩存獲取 bean 實例對象,見代碼塊7詳解

6.通過 MergedBeanDefinition 來檢查 beanName 對應的 bean 是否爲 FactoryBean。首先通過 getMergedLocalBeanDefinition 方法獲取 beanName 的 MergedBeanDefinition,該方法在代碼塊2已經解析過;接着調用 isFactoryBean 來檢查 beanName 對應的 bean 是否爲 FactoryBean,見代碼塊8詳解。


代碼塊7:getSingleton

這段代碼很重要,在正常情況下,該代碼很普通,只是正常的檢查下我們要拿的 bean 實例是否存在於緩存中,如果有就返回緩存中的 bean 實例,否則就返回 null。

這段代碼之所以重要,是因爲該段代碼是 Spring 解決循環引用的核心代碼。

 

解決循環引用邏輯:使用構造函數創建一個 “不完整” 的 bean 實例(之所以說不完整,是因爲此時該 bean 實例還未初始化),並且提前曝光該 bean 實例的 ObjectFactory(提前曝光就是將 ObjectFactory 放到 singletonFactories 緩存),通過 ObjectFactory 我們可以拿到該 bean 實例的引用,如果出現循環引用,我們可以通過緩存中的 ObjectFactory 來拿到 bean 實例,從而避免出現循環引用導致的死循環。這邊通過緩存中的 ObjectFactory 拿到的 bean 實例雖然拿到的是 “不完整” 的 bean 實例,但是由於是單例,所以後續初始化完成後,該 bean 實例的引用地址並不會變,所以最終我們看到的還是完整 bean 實例。

這段解決邏輯涉及到了後面的一些內容,所以可能會看的不是很理解,建議吧整個bean創建看完再回頭看。。。。。

這個代碼塊中引進了4個重要緩存:

1、singletonObjects 緩存:beanName -> 單例 bean 對象。
2、earlySingletonObjects 緩存:beanName -> 單例 bean 對象,該緩存存放的是早期單例 bean 對象,可以理解成還未進行屬性填充、初始化。
3、singletonFactories 緩存:beanName -> ObjectFactory。
4、singletonsCurrentlyInCreation 緩存:當前正在創建單例 bean 對象的 beanName 集合。
singletonObjectsearlySingletonObjectssingletonFactories 在這邊構成了一個類似於 “三級緩存” 的概念。

代碼塊8:isFactoryBean


1.拿到 beanName 對應的 bean 實例的類型,見代碼塊9詳解

代碼塊9:predictBeanType

這邊走的是 AbstractAutowireCapableBeanFactory 裏的方法,而不是 AbstractBeanFactory 裏的方法。通過前面的介紹,我們知道創建的 BeanFactory 爲 DefaultListableBeanFactory,而 DefaultListableBeanFactory 繼承了 AbstractAutowireCapableBeanFactory,因此這邊會走 AbstractAutowireCapableBeanFactory 的重寫方法。
 

 

劃重點:

本文執行了創建 bean 實例前的一些準備操作。主要是引入了 FactoryBean 這一特殊的 bean,獲取 BeanDefinition 的 MergedBeanDefinition,最後將 BeanDefinition 統一轉換成 RootBeanDefinition

另外,本文還引入了幾個重要的緩存,如下:

mergedBeanDefinitions 緩存:beanName -> 合併的 bean 定義。
beanDefinitionMap 緩存:beanName -> BeanDefinition。
singletonObjects 緩存:beanName -> 單例 bean 對象。
earlySingletonObjects 緩存:beanName -> 單例 bean 對象,該緩存存放的是早期單例 bean 對象,可以理解成還未進行屬性填充、初始化。
singletonFactories 緩存:beanName -> ObjectFactory。
singletonsCurrentlyInCreation 緩存:當前正在創建單例 bean 對象的 beanName 集合。
Spring 中大量使用了本地緩存,基本上通過名字和註釋就能理解緩存的作用了。

後面的文章中,我們重點介紹核心方法-----doGetBean(獲取bean方法)

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