Spring源碼學習筆記9——構造器注入及其循環依賴
Spring源碼學習筆記9——構造器注入及其循環依賴
一丶前言
前面我們分析了spring基於欄位的和基於set方法注入的原理,但是沒有分析第二常用的注入方式(構造器注入)(第一常用欄位注入),並且在循環依賴問題上構造器注入常被說spring無法解決構造器注入的循環依賴,下面我們來分析構造器注入和其循環依賴的源碼
二丶構造器依賴注入
在spring初始化每一個非抽象,單例,非懶載入的bean的時候,會調用createBeanInstance方法去創建bean的實例,在使用默認的策略——無參構造or CGLIB生成子類對象的方式之前,先會使用所有的SmartInstantiationAwareBeanPostProcessor 來判斷是否有用戶指定的後置處理器
1.循環調用所有的SmartInstantiationAwareBeanPostProcessor實現類來推斷該使用哪個構造器
這裡重點是使用到了AutowiredAnnotationBeanPostProcessor來推斷
2.AutowiredAnnotationBeanPostProcessor 推斷構造器的邏輯
首先AutowiredAnnotationBeanPostProcessor 有一個快取key是bean類型,value是之前推斷的所有構造器數組。自然是一波雙if+synchronized方式來讀快取中內容,快取沒有命中再進行推斷
遍歷每一個構造器(這裡省去了spring對Kotlin語言支援的部分)
1.獲取當前構造器上面的@Autowired 和 @Value 註解
2.如果註解資訊為空,且當類是一個被CGLIB代理後的類,
會獲取父類構造器上面的註解(要求父類構造器和當前遍歷到的構造器參數類型順序相同)
3.如果註解資訊不為空,會檢查 required 屬性的資訊
(spring還支援ejb等的註解,所有這裡檢查require的資訊)像@Autowired 和 @Value這種不包含required屬性的註解會默認是required為true,
並且記錄當前構造器為候選者,也就是說有註解的才算在候選者
4.如果存在多個required=true的構造器拋出異常,
否則使用遍歷記錄當前required=true的構造器
5.如果候選者不為空,required=true的構造器為空
(一般這種情況是標註了EJB的註解指定required為false)spring會把無參構造加入到候選者中
6.如果原來類只定義了一個有參構造,那麼使用這個有參構造
如果不考慮EJB中的依賴注入註解
那麼就是如果構造器有@Autowired or @Value那麼默認使用它
如果定義了一個有參構造那麼使用這個有參構造
其他情況一律返回null(後續spring可能使用無參構造or CGLIB生成子類的方式)
3.使用構造器進行依賴注入的邏輯
推斷出使用哪個構造器之後,spring調用autowireConstructor方法進行構造器注入,具體邏輯委託給ConstructorResolver的autowireConstructor方法,最終解析依賴注入參數的在createArgumentArray方法中進行,一般是調用resolveAutowiredArgument方法
最終還是殊途同歸的調用到了beanFactory的resolveDependency方法
4.實例化對象
這一步還是調用的instantiate方法
三丶為什麼構造器的循環依賴spring無法解決
1.構造方法注入無法解決循環依賴的情況及其原因
如果是這種循環依賴的情況spring是無法解決循環依賴的,下面是這種循環依賴出現時候的程式碼流程
問題出現在beforeSingletonCreation方法中
在spring創建bean的之前使用了set維護正在創建bean的名稱,B需要A,再次創建A的時候就發現set中原本存在A,這時候就是無法解決循環依賴的情況,會拋出異常
同理這種情況也是不可以解決的
2.構造方法注入可以解決循環依賴的情況及其原因
這種情況下spring又是可以解決循環依賴的
原因是A是可以成功使用無參構造實例化的,所以B需要A的時候可以在三級快取拿到A的ObjectFactory,調用後得到A,而不是再次創建A
我們可以得到結論
如果載入順序是A然後B,那麼A只能是欄位注入or方法注入,不能是構造器注入
B無所謂那種注入方式