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無所謂那種注入方式
Tags: