目的:原始碼除錯構造器注入,看看是怎么報錯的,
環境: spring:5.2.3 jdk:1.8
(小弟第一次寫,知道有待改進,路過的各位大哥,在評論里給點意見,我會逐步完善的,謝謝了~)
一、準備
首先準備兩個回圈依賴的類:userService和roleService
<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時候,準確的說,是實體化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. org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons()
- 獲取BeanDefinition集合,可以看到userService和roleServic已經在容器中

- 回圈遍歷每個BeanDefinition,判讀是不是非抽象的&&單例的&&懶加載的 bean,
- 如果是工廠bean,走處理工廠的方法;如果普通bean,呼叫getBean方法,
2. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean()


- 在doGetBean方法中,直接找到創建單例物件的這部分代碼,如下圖:

3. org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()

- 首先看一下注釋:回傳給定名字的單例物件,如果沒有注冊過,那么就創建并注冊,
- ObjectFactory<?>是什么?它就是一個簡單工廠,可以回傳物件實體,怎么用,下面說,

- this.singletonObjects,快取已經實體化的單例物件的集合,userService還沒有實體化,
- 在創建單例bean之前要做些事情:檢查這個bean是否可以被創建,呼叫beforeSingletonCreation(beanName)邏輯如下:

- inCreationCheckExclusions:要排除的bean的集合,userService不是要排除的bean,這個判斷是true,
- singletonsCurrentlyInCreation:正在創建的單例集合,把userService放在要被創建的集合,插入成功是true,前面還有一個‘!’,所以這個判斷是false,不拋出例外,
- 看到這里基本可以明白了,回圈依賴就是這里拋出的例外:第二次檢查userService時,userService在singletonsCurrentlyInCreation集合中,說明userService正準備實體化,發生已經回圈依賴了,

- 通過上面的檢查,userService可以被創建,呼叫singletonFactory.getObject();獲取物件,即從ObjectFactory<?>這個物件工廠中獲取userService,
- 問題來了,這個工廠(singletonFactory)哪來的?怎么作業的?看函式宣告:

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

- 原來是createBean(beanName, mbd, args)的回傳值,那么就是說明createBean會回傳一個工廠物件,這個工廠物件只生產userService這一個物件,
4.org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()

- 在createBean方法中,找到doCreateBean方法:
5.org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean()

- 呼叫createBeanInstance(beanName, mbd, args)實體化bean
6.org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance()

- 這里會判斷是不是構造方法注入,點進去:

- 這里有兩個方法,創建一個構造方法決議器,然后決議構造方法
7.org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor()

- 在這里決議到userService的構造方法中,有個引數是RoleService型別的,繼續向下走:

- 這里會決議構造方法的引數,點進去:

- 決議構造方法引數的值,即引數名稱,決議出來是roleService,決議程序如下:

- 呼叫resolveReference(argName, ref)方法,獲取roleService的實體,然后和userService關聯起來,獲取roleService代碼如下:

- 可以看到,容器呼叫getBean、doGetBean等方法,獲取roleService,
- 像userService一樣,分析roleSerice的結果是:roleSerice依賴userService,容器又去獲取userService,跳過roleService的程序,直接到達第二次獲取userService:

- 直接跳轉到出現問題的地方,檢查userService是否可以實體化:

- 這時候發現,準備實體化的集合里面有userService,再次插入userService失敗,說明發生了回圈依賴,容器就報錯了,
三、結論:
singletonsCurrentlyInCreation的作用:記錄準備好實體化的單例bean,如果里面出現了重復的bean,說明有回圈依賴,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/242671.html
標籤:Java
