spring原始碼之bean的初始化及迴圈引用

開心的魚a1發表於2020-09-29

例項化方法,把bean例項化,並且包裝成BeanWrapper

1、點進這個方法裡面。

 

 

 

這個方法是反射呼叫類中的 factoryMethod 方法。 這要知道@Bean 方法的原理, 實際上
spring 會掃描有@bean 註解的方法, 然後把方法名稱設定到 BeanDefinition factoryMethod
屬性中, 接下來就會調到上面截圖中的方法實現@Bean 方法的呼叫。 
2、 有參建構函式的時候

determineConstructorsFromBeanPostProcessors
這個方法是 BeanPostProcessor 介面類的首次應用, 最終會掉到
AutowiredAnnotationBeanPostProcessor 類的方法, 在方法中會掃描有註解的建構函式然後完
成裝配過程。 然後把有有@Autowired 註解的建構函式返回。

3、 無參建構函式的例項化

 

 

 這就是簡單的反射例項化。 大部分類的例項化都會走這個邏輯
4、 類中註解的收集
例項化完成後接下來就需要對類中的屬性進行依賴注入操作, 但是類裡面屬性和方法的依
賴注入往往用@Autowired 或者@Resource 註解, 那麼這些註解的依賴注入是如何完成的
呢?
註解的收集:

 

 

 也是通過 BeanPostProcessor 介面型別例項來挨個處理的。
A、 首先是
CommonAnnotationBeanPostProcessor 類, 這個類完成了@Resource 註解的屬性或
者方法的收集
這個類還對@PostConstruct @PreDestory 支援

 

 

 

 

 

 收集過程
1、 看快取裡面有沒有 InjectionMetadata 物件
2、 從類中獲取所有 Field 物件, 迴圈 field 物件, 判斷 field 有沒有@Resource 註解,
如果有註解封裝成 ResourceElement 物件
3、 從類中獲取所有 Method 物件, 迴圈 Method 物件, 判斷 Method 有沒有@Resource
註解, 如果有註解封裝成 ResourceElement 物件
4、 最終把兩個 field Method 封裝的物件集合封裝到 InjectionMetadata 物件中

B、 然後是
AutowiredAnnotationBeanPostProcessor 類, 對@Autowired 註解的屬性和方法
的收集。 收集過程基本上跟@Resource 註解的收集差不多, 這裡就不贅述了

 

5IOC\DI
對應的方法:

 

 

 這裡又是一個 BeanPostProcessor 型別介面的運用, 前面我們講到了@Resource@Autowired 註解的收集, 那麼這個方法就是根據收集到的註解進行反射調用。 

 

 

 

 

 迴圈收集到的 metaData 中的 list 物件, 然後挨個呼叫裡面的 InjectedElement inject 方法完成依賴注入。 

 

 

 

 

 其中 value 值的獲取, 如果依賴的屬性是一個引用型別必定會觸發該屬性的
BeanFactory.getBean 操作, 從而從 spring 容器中獲取到對應的例項。 方法的依賴注
入類似這裡就不再贅述。

6bean 例項化後的操作
程式碼走到這裡:

 

 

 A、 首先是對某些 Aware 介面的呼叫

 

 

B、 然後@PostConstruct 註解方法的呼叫

 

 

 這裡又是一個 BeanPostProcessor 介面的運用,
前面講過, 有@PostConstruct 註解的方法會收集到一個 met
就是通過 BeanPostProcessor 介面調到
CommonAnnotationBeanPostProcessor 類, 然後在類中拿
根據物件裡面的容器來反射呼叫有註解的方法。 程式碼如下:

 

 

 @PostConstruct 註解的容器會收集到 initMethods 容器中, 接下來就是方法的

反射呼叫。

 

 

 

CInitializingBean 介面和 init-method 屬性呼叫

 

 

 Init-method 屬性呼叫是在 afterPropertiesSet 之後

 

 

 afterPropertiesSetInit-method和有@PostConstruct註解的方法其實核
心功能都是一樣的, 只是呼叫時序不一樣而已, 都是在該類例項化和 IOC 做完後呼叫
的, 我們可以在這些方法中做一些在 spring 或者 servlet 容器啟動的時候的初始化
工作。 比如快取預熱, 比如快取資料載入到記憶體, 比如配置解析, 等等初始化工作。
在這個方法裡面還有一個重要的邏輯

 

 

 

也是一個 BeanPostProcessor 介面的運用, 在這裡會返回 bean 的代理例項, 這個
就是 AOP 的入口。
DFactoryBean 介面
帶入如下:

 

 

 

 在例項化和 IOC/DI 做完後, 就會呼叫 FactoryBean 型別的介面, 如果要獲取到
FactoryBean 類本身, 就必須加上”&”符號, 比如
beanFactory.getBean(&beanName)


 

 

  BeanFactory.getBean(beanName” )只能獲取到 getObject()方法返回的實
例。
getObject 方法返回的例項會有單獨的快取儲存, 跟其他例項不是同一個快取, 對應的緩
存是: factoryBeanObjectCache 

E、 迴圈依賴
迴圈依賴請參照流程圖理解
https://www.processon.com/view/link/5df9ce52e4b0c4255ea1a84f
迴圈依賴只會出現在單例例項無參建構函式例項化情況下
有參建構函式的加@Autowired 的方式迴圈依賴是直接報錯的, 多例的迴圈依賴也是
直接報錯的

 

 

 

 

 迴圈依賴步驟:
1A 類無參建構函式例項化後, 設定三級快取
2A populateBean 進行依賴注入, 這裡觸發了 B 類屬性的 getBean 操作
3B 類無參建構函式例項化後, 設定三級快取
4B populateBean 進行依賴注入, 這裡觸發了 A 類屬性的 getBean 操作
5A 類之前正在例項化, singletonsCurrentlyInCreation 集合中有已經
有這個 A 類了, 三級快取裡面也有了, 所以這時候是從三級快取中拿到的提前暴露的
A 例項, 該例項還沒有進行 B 類屬性的依賴注入的, B 類屬性為空。
6B 類拿到了 A 的提前暴露例項注入到 A 類屬性中了
7B 類例項化已經完成, B 類的例項化是由 A 類例項化中 B 屬性的依賴注入觸發
getBean 操作進行的, 現在 B 已經例項化, 所以 A 類中 B 屬性就可以完成依賴注
入了, 這時候 A B 屬性已經有值了
8B A 屬性指向的就是 A 類例項堆空間, 所以這時候 B A 屬性也會有值了。

 

相關文章