目的:原始碼除錯構造器注入,看看是怎麼報錯的。
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. 判斷是否需要例項化
- 迴圈遍歷每個BeanDefinition,判讀是不是 非抽象的&&單例的&&懶載入的 bean,不是跳過。
- 判斷是不是工廠bean,是工廠bean,走處理工廠的方法。
- 不是工廠bean,是普通bean,初始化,繼續向下
getSingleton(beanName);作用:從快取中獲取單例Bean。
singletonObjects用於快取單例物件。從singletonObjects獲取userService。由於是第一次獲取,裡面肯定沒有。
呼叫isSingletonCurrentlyInCreation(beanName)方法,判斷是不是在初始化中:
singletonsCurrentlyInCreation用於儲存正在初始化的Bean。正在初始化的集合裡為空,返回false
- isSingletonCurrentlyInCreation(beanName)返回false。
- getSingleton(beanName);沒有獲取到已經初始化的bean,返回null。
- 沒有獲得單例bean,走else方法
- else程式碼塊中,上面一堆判斷不重要,直接看下面。markBeanAsCreated方法標記這個bean,準備好建立了。
alreadyCreated使用者存放準備好初始化的Bean.
回到原來的方法,又是略過一段程式碼,判斷userService是不是單例bean,肯定是,走這個方法。
- 是單例bean,走這個分支。
- 又是一個getSingleton方法,注意:這個是方法是兩個引數,一個是beanName,一個是ObjectFactory<?>物件工廠,如下圖:
- 首先看一下注釋:返回給定名字的單例物件,如果沒有註冊過,那麼就建立並註冊。註冊是什麼??留一個疑問。
- ObjectFactory<?>是什麼?它就是一個簡單工廠,可以返回物件例項,怎麼用,下面說。
- this.singletonObjects,又是它:快取單例物件的集合。上面獲取一遍,沒有,這次還是沒有。
- 在建立單例bean之前要做些事情:檢查這個bean是否可以被建立,邏輯如下:
- inCreationCheckExclusions:要排除的bean的集合。userService不是要排除的bean,這個判斷是true。
- singletonsCurrentlyInCreation:正在建立的單例集合。把userService放在要被建立的集合,插入成功是true,前面還有一個‘!’,所以這個判斷是false。不丟擲異常。看到這裡基本可以明白個大概了,迴圈依賴就是這裡丟擲的異常:二次檢查userService時,userService在singletonsCurrentlyInCreation,說明userService正準備初始化,已經迴圈依賴了。可繼續往下看:
檢查沒問題,userService可以被建立,通過工廠獲取物件。從ObjectFactory<?>這個物件工廠中獲取bean:singletonFactory.getObject();
問題來了,這個工廠(singletonFactory)哪來的?怎麼工作的?
它是傳進來的引數,怎麼傳進來的,再看看呼叫:
原來是createBean(beanName, mbd, args);返回的。那麼就是說明createBean會返回一個工廠物件,這個工廠物件只生產userService這一個物件。
點進createBean方法,找到doCreateBean方法
- 在這個類裡。
- 看註釋,實際建立Bean的方法。跟蹤除錯看一下:
- 先從快取中去掉,
- 再建立一個新的,點進去
將BeanDefinition解析成Class,忽略上面這個圖停留的地方,繼續向下:
判斷是構造器注入的,點進去:
這裡有兩個方法,走第二個,構造器解析:
- 在構造器解析類中
- 解析userService中有一個構造器
- 構造器引數型別是RoleService,繼續向下走:
解析構造器引數,點進去:
解析構造器引數的值,即引數名稱:
解析出來是roleService。容器會先將依賴的bean初始化好。然後關聯起來。
從這裡開始,又是一頓分析roleSerice,過程和userService一樣。分析到的結果是:roleSerice依賴userService。又去獲取userService:
這個胡漢三又回來了!
然後又是getSingleton(),往下看:
現在準備初始化的bean集合有兩個了,而且就有userService,下面看容器時怎麼報錯的:
又是往下走:到了這裡
又檢查userService時候可以初始化:
準備初始化的集合裡面有userService,報錯了!