Spring原始碼--debug分析迴圈依賴--構造器注入

chris_lou_yang發表於2020-12-30

目的:原始碼除錯構造器注入,看看是怎麼報錯的。

spring:5.2.3

jdk:1.8

一、準備

  首先準備兩個迴圈依賴的類:userService和roleServic

<bean id="userService" class="com.chris.spring.service.UserServiceImpl">
    <constructor-arg ref="roleService"/>
</bean>
<bean id="roleService" class="com.chris.spring.service.RoleService">
   <constructor-arg ref="userService"/>
</bean>

二、開始除錯

  因為依賴注入的觸發點是容器初始化所有非懶載入Bean時候,所以可以直接refresh()方法中的finishBeanFactoryInitialization(beanFactory);方法看起。

 1     /**
 2      * Finish the initialization of this context's bean factory,
 3      * initializing all remaining singleton beans.
 4      */
 5     protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
       ......
       //初始化所有非懶載入的單例bean, 32 // Instantiate all remaining (non-lazy-init) singletons. 33 beanFactory.preInstantiateSingletons(); 34

1.獲取BeanDefinition集合

 說明:可以看到userService和roleServic已經在容器中,準備初始化了。繼續:

2. 判斷是否需要例項化

  1.  迴圈遍歷每個BeanDefinition,判讀是不是  非抽象的&&單例的&&懶載入的  bean,不是跳過。
  2. 判斷是不是工廠bean,是工廠bean,走處理工廠的方法。
  3. 不是工廠bean,是普通bean,初始化,繼續向下

 

 

getSingleton(beanName);作用:從快取中獲取單例Bean。

 

 

 singletonObjects用於快取單例物件。從singletonObjects獲取userService。由於是第一次獲取,裡面肯定沒有。

 

 

 呼叫isSingletonCurrentlyInCreation(beanName)方法,判斷是不是在初始化中:

 

 

 singletonsCurrentlyInCreation用於儲存正在初始化的Bean。正在初始化的集合裡為空,返回false

 

 

  1.  isSingletonCurrentlyInCreation(beanName)返回false。
  2. getSingleton(beanName);沒有獲取到已經初始化的bean,返回null。

  1.  沒有獲得單例bean,走else方法
  2. else程式碼塊中,上面一堆判斷不重要,直接看下面。markBeanAsCreated方法標記這個bean,準備好建立了。

 

 

 

alreadyCreated使用者存放準備好初始化的Bean.

 

 

 

 回到原來的方法,又是略過一段程式碼,判斷userService是不是單例bean,肯定是,走這個方法。

 

 

  1.  是單例bean,走這個分支。
  2. 又是一個getSingleton方法,注意:這個是方法是兩個引數,一個是beanName,一個是ObjectFactory<?>物件工廠,如下圖:

 

 

  1.  首先看一下注釋:返回給定名字的單例物件,如果沒有註冊過,那麼就建立並註冊。註冊是什麼??留一個疑問。
  2. ObjectFactory<?>是什麼?它就是一個簡單工廠,可以返回物件例項,怎麼用,下面說。

 

 

  1. this.singletonObjects,又是它:快取單例物件的集合。上面獲取一遍,沒有,這次還是沒有。
  2. 在建立單例bean之前要做些事情:檢查這個bean是否可以被建立,邏輯如下:

 

 

  1.  inCreationCheckExclusions:要排除的bean的集合。userService不是要排除的bean,這個判斷是true。
  2. singletonsCurrentlyInCreation:正在建立的單例集合。把userService放在要被建立的集合,插入成功是true,前面還有一個‘!’,所以這個判斷是false。不丟擲異常。看到這裡基本可以明白個大概了,迴圈依賴就是這裡丟擲的異常:二次檢查userService時,userService在singletonsCurrentlyInCreation,說明userService正準備初始化,已經迴圈依賴了。可繼續往下看:

 

 

 

 檢查沒問題,userService可以被建立,通過工廠獲取物件。從ObjectFactory<?>這個物件工廠中獲取bean:singletonFactory.getObject();

問題來了,這個工廠(singletonFactory)哪來的?怎麼工作的?

 

 

 它是傳進來的引數,怎麼傳進來的,再看看呼叫:

原來是createBean(beanName, mbd, args);返回的。那麼就是說明createBean會返回一個工廠物件,這個工廠物件只生產userService這一個物件。

點進createBean方法,找到doCreateBean方法

 

 

  1.  在這個類裡。
  2. 看註釋,實際建立Bean的方法。跟蹤除錯看一下:

 

 

  1.  先從快取中去掉,
  2. 再建立一個新的,點進去

 

 

 將BeanDefinition解析成Class,忽略上面這個圖停留的地方,繼續向下:

 

 

 判斷是構造器注入的,點進去:

 

 

 這裡有兩個方法,走第二個,構造器解析:

 

 

  1.  在構造器解析類中
  2. 解析userService中有一個構造器
  3. 構造器引數型別是RoleService,繼續向下走:

 

 

解析構造器引數,點進去:

 

 

 解析構造器引數的值,即引數名稱:

 

 

 解析出來是roleService。容器會先將依賴的bean初始化好。然後關聯起來。

 

 

 從這裡開始,又是一頓分析roleSerice,過程和userService一樣。分析到的結果是:roleSerice依賴userService。又去獲取userService:

 

 

 這個胡漢三又回來了!

 

然後又是getSingleton(),往下看:

 

 

 

 現在準備初始化的bean集合有兩個了,而且就有userService,下面看容器時怎麼報錯的:

又是往下走:到了這裡

 

 

 又檢查userService時候可以初始化:

 

 

 準備初始化的集合裡面有userService,報錯了!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章