前言
初夏時節, 時間: AM 7.30分左右, 空無一人的健身房裏,一個碩大的身體在跑步機上扭動着, 不一會頭上便揮汗如雨, 他嘴上還不時嘀咕着 “循環依賴,單例模式,Bean的定位加載註冊,原型模式…”, 漸漸跑着跑着就變成了慢慢悠悠的走走歇歇,忽然間感覺肩膀好像被磚頭砸了一下,
身後傳來一句
"你張大胖減肥是不可能減肥的,就是在健身房裏劃劃水才能維持的了生活 !" ,大胖被拍的渾身一驚, 看了眼一身運動裝的 Mason, 急忙反駁道
"沒有調查就沒有發言權, 俺只是暫時休息下, 今天都已經跑了好幾個小時了呢,倒是 Mason 你像是來假健身的…",Mason 笑笑說 "哎呀呀, 你這麼厲害呢,可惟獨肚子上的贅肉騙不了人啊,最近面試的怎麼樣?"
大胖 一臉愁容的說道: "最近招聘市場很給力,我也參加了不少面試,就是每次聊到 Spring 時被面試官三連追問 Spring是如何解決循環依賴, 而我對着個問題查了很多資料,但也就能回答個一知半解,然後就叫我回去等通知了…"
Mason 在跑步機上邊跑邊說 "Spring 解決循環依賴的這個場景, 其實也可以映射到生活中, 比如 你工作日睡過了又害怕遲到扣錢,就在 DD出行 App上選擇一個起始地打車, 同一時間來了兩輛 DD專車, 你也沒有check車牌就上車了, 司機也沒check你拉上就走,你一上車就聚精會神看起宅舞視頻, 結果到達別人的目的地發現上錯車,既遲到了又要付來回路費,那麼問題來了 你爲什麼會上錯車呢? "
大胖 撓撓頭回答道: "因爲睡過了怕遲到啊! 呸,不對, 因爲要早起去打工!"
Mason 白了張大胖一眼說: "起不來是因爲你天天熬夜,坐錯車是因爲兩個原因: 1.沒有check車牌 2.DD專車不止一輛"
大胖 一臉懵X 的問道 "繞了半天, 那這上錯車和Spring創建Bean時的循環依賴又有什麼關係呢 ?"
Mason 一臉這孩子沒救了的表情回答道 "循環依賴的觸發條件就是, 你上了別人的DD專車, 而你打DD專車, 判斷是否循環依賴就需要 check車牌,如果要徹底根治循環依賴就必須讓世界上的 DD專車 只有一輛. Spring 中的循環依賴也是同理!"
見微知著
我們暫不討論 Spring 的循環依賴, 先看一道 LeetCode 題目 141. 環形鏈表
擴展: 在多線程環境下使用JDK1.7中的HashMap, 併發調用resize()時會出現環形鏈表,後再get()會導致CPU 100%, 那我們該如何去判斷環形鏈表呢?
給定一個鏈表,判斷鏈表中是否有環。
爲了表示給定鏈表中的環,我們使用整數 pos
來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是
-1,則在該鏈表中沒有環。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。
示例 3:
輸入:head = [1], pos = -1
輸出:false
解釋:鏈表中沒有環。
那麼 Spring 中是用那種方式發現 循環依賴的呢 ? (文章結尾揭曉答案)
// Definition for singly-linked list.
public class ListNode {
public int val;
public ListNode next;
public ListNode(int x) {
val = x;
}
}
Set判重
public boolean hasCycle_Set(ListNode head) {
// 如果入參鏈表過短則不存在環
if (head == null || head.next == null) {
return false;
}
HashSet<ListNode> set = new HashSet<>();
// 如果 遍歷到最後任然沒有發現環則不存在環
while (head.next != null){
// 將每一個遍歷過的元素存入,之後判重
if (set.contains(head)){
return true;
}
set.add(head);
head = head.next;
}
return false;
}
快慢指針判重
public boolean hasCycle_QuickSlowPointer (ListNode head) {
// 如果入參鏈表過短則不存在環
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
// 如果 快指針遍歷到最後仍然沒有發現環則不存在環
while (fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
// 快指針每輪走兩步 慢指針每輪走一步 如果有環快慢指針最終就會相遇
if (slow == fast){
return true;
}
}
return false;
}
新整了個活, 今後長期維護的一個LeetCode題解庫,歡迎 Star
Spring 中常用的兩種 Bean DI 方式的循環依賴示例
Spring DI (Dependency Injection 依賴注入) 是指 Bean 被動接受其他 Bean的依賴注入而不自己主動去找, 簡而言之
Bean 不會從容器中查找它依賴的 Bean , 而是靠容器實例化 Bean 時由容器將它依賴的 Bean 注入, 此舉與Java 類實例化流程相反.
構造器 DI 示例代碼(基於Spring 5.1.6)
package org.springframework.context.annotationX.circular.constructor;
import org.springframework.stereotype.Component;
/**
* Spring Constructor DI 循環依賴 Bean1 Demo
*/
@Component
public class Bean1ConstructorBean2Demo {
private Bean2ConstructorBean1Demo bean2;
public Bean1ConstructorBean2Demo(Bean2ConstructorBean1Demo bean2DependBean1Demo) {
this.bean2 = bean2DependBean1Demo;
}
public void hello() {
bean2.hello();
}
}
package org.springframework.context.annotationX.circular.constructor;
import org.springframework.stereotype.Component;
/**
* Spring Constructor DI 循環依賴 Bean2 Demo
*/
@Component
public class Bean2ConstructorBean1Demo {
private Bean1ConstructorBean2Demo bean1;
public Bean2ConstructorBean1Demo(Bean1ConstructorBean2Demo bean1DependBean2Demo1) {
bean1 = bean1DependBean2Demo1;
}
public void hello() {
System.out.println("Run Circular Dependency Success");
}
}
註解自動裝配 DI 示例代碼
package org.springframework.context.annotationX.circular.autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Spring @Autowired DI 循環依賴 Bean1 Demo
*/
@Component
public class Bean1AutowiredBean2Demo {
@Autowired
private Bean2AutowiredBean1Demo bean2;
public void hello() {
bean2.hello();
}
}
package org.springframework.context.annotationX.circular.autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Spring @Autowired DI 循環依賴 Bean2 Demo
*/
@Component
public class Bean2AutowiredBean1Demo {
@Autowired
private Bean1AutowiredBean2Demo bean1;
public void hello(){
System.out.println("Run Circular Dependency Success");
}
}
兩種 DI 方式的單元測試代碼
package org.springframework.context;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotationX.circular.autowired.Bean1AutowiredBean2Demo;
import org.springframework.context.annotationX.circular.constructor.Bean1ConstructorBean2Demo;
/**
* Created by 以鬥爭求團結則團結存,以退讓求團結則團結亡 !
*/
public class AnnotationCircularDependencyTestX {
@Test
public void diBeanByAutowired() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("org.springframework.context.annotationX.circular.autowired");
context.refresh();
Bean1AutowiredBean2Demo bean1 = (Bean1AutowiredBean2Demo) context.getBean("bean1AutowiredBean2Demo");
bean1.hello();
}
@Test
public void diBeanByConstructor () throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("org.springframework.context.annotationX.circular.constructor");
context.refresh();
Bean1ConstructorBean2Demo bean1 = (Bean1ConstructorBean2Demo) context.getBean("bean1ConstructorBean2Demo");
bean1.hello();
}
}
猜猜上面那種 DI 方式打印了 “Run Circular Dependency Success” 以及它解決循環依賴的方式 ?
答案: 註解自動裝配 DI 示例代碼 打印成功 而 構造器 DI 示例代碼因爲循環依賴運行失敗 , 那這兩種方式有什麼區別呢 !
(構造器 DI 錯誤日誌)
Unsatisfied dependency expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
'bean2ConstructorBean1Demo' defined in file
[D:\SpringFamily-SourceCodeStudy\Spring-Framework\spring-framework-5.1.6.REL
EASE\spring-context\out\test\classes\org\springframework\context\annotationX\circular\constructor\Bean2Const
ructorBean1Demo.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name
'bean1ConstructorBean2Demo': Requested bean is currently in creation: Is there an unresolvable circular
reference?
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
....
-
註解自動裝配 DI 方式
-
優點:
-
基於三層緩存不會發生循環依賴
-
自描述清晰
-
-
缺點:
- 對構建單元測試不友好
-
-
構造器 DI 方式
- 優點:
- 代碼結構明確,可讀性高
- 構建單元測試友好
- 非IOC容器環境可使用new實例化該類的對象。
- 缺點:
- 會發生循環依賴
- 當注入參數較多時,代碼臃腫。
- 優點:
對於循環依賴問題,Spring根據注入方式,採取不同的處理策略,如果依賴雙方都是使用屬性值注入或者Setter方法注入,則Spring可以自動解決循環依賴注入問題,Spring 程序可以成功啓動;如果依賴雙方是使用構造函數注入對方或者主Bean對象使用構造函數注入或循環注入的Bean都是原型模式,則Spring 無法解決循環依賴注入 ,Spring程序報循環依賴無法啓動。
註解 DI 方式 的運行成功原因
註解 DI 方式與 構造器 DI 方式 最終要達到的目的相同, 但Spring 對它倆的實現卻不太一樣,
Spring-Context 在 AbstractApplicationContext.refresh()
方法完成 Bean 的關鍵生命週期 IOC, 實例化, DI 等,
DI 具體是 IOC 完成後調用 finishBeanFactoryInitialization()
方法, 具體方法邏輯如下
-
能觸發依賴注入的條件有倆: 1. 第一次調用 getBean 方法時, 2. 懶加載被預實例化時, 此方法滿足了其中第一條.
-
1.根據 beanDefinitionMap 依次判斷 Bean 是否 Lazy, 是否 Prototype, 是否 Abstract 等等
-
2.接下來根據判斷結果 填充構造方法來反射 Bean 的從而實例化. 所以在此之前必須推斷Bean的構造方法.
-
3.反射實例化一個對象;注意我這裏說的是對象、對象、對象;不是並不是一個完整的bean,因爲 對象屬性是沒有注入,所以不是一個完整的bean;
-
4.Spring 處理 合併後的 BeanDefinition.
-
5.判斷是否支持 循環依賴 如果支持則提前把 一個工廠存入
singletonFactories
Map<String, ObjectFactory<?>> -
6.進行屬性注入
-
7.回調 Aware 接口, 生命週期回調方法, 是否需要代理
-
8.put 到 Spring容器
-
文章最後會帶大家詳細閱讀上述源碼.
成功原因:
// 是否需要提前曝光,用來解決循環依賴時使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 這裏是一個匿名內部類, 爲了循環引用, 儘早持有對象的引用
// 解決循環依賴 第二個參數是回調接口,實現的功能是將切面動態織入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 判斷 singletonObjects 不存在 beanName
if (!this.singletonObjects.containsKey(beanName)) {
// 放入 beanName -> beanFactory,到時在 getSingleton() 獲取單例時,可直接獲取創建對應 bean 的工廠,解決循環依賴
this.singletonFactories.put(beanName, singletonFactory);
// 從提前曝光的緩存中移除,之前在 getSingleton() 放入的
this.earlySingletonObjects.remove(beanName);
// 往註冊緩存中添加 beanName
this.registeredSingletons.add(beanName);
}
}
}
先來看 earlySingletonExposure
這個變量: 從字面意思理解就是需要提前曝光的單例。
有以下三個判斷條件:
mbd
是否是單例- 該容器是否允許循環依賴
- 判斷該
bean
是否在創建中。
如果這三個條件都滿足的話,就會執行 addSingletonFactory
操作。要想着,寫的代碼都有用處,所以接下來看下這個操作解決的什麼問題和在哪裏使用到吧
Bean1AutowiredBean2Demo
類中含有屬性 Bean2AutowiredBean1Demo
,Bean2AutowiredBean1Demo
類中含有屬性 Bean1AutowiredBean2Demo
,這兩個類在初始化的時候經歷了以下的步驟:
- 創建
Bean1AutowiredBean2Demo
,先記錄對應的beanName
然後將Bean1AutowiredBean2Demo
的創建工廠 beanFactoryA 放入緩存中 - 對
Bean1AutowiredBean2Demo
的屬性填充方法populateBean
,檢查到依賴Bean2AutowiredBean1Demo
,緩存中沒有Bean2AutowiredBean1Demo
的實例或者單例緩存,於是要去實例化Bean2AutowiredBean1Demo
。 - 開始實例化
Bean2AutowiredBean1Demo
,經歷創建Bean2AutowiredBean1Demo
的過程,到了屬性填充方法,檢查到依賴了Bean1AutowiredBean2Demo
。 - 調用
getBean(Bean1AutowiredBean2Demo)
方法,在這個函數中,不是真正去實例化Bean1AutowiredBean2Demo
,而是先去檢測緩存中是否有已經創建好的對應的bean
,或者已經創建好的beanFactory
- 檢測到
beanFactoryA
已經創建好了,而是直接調用ObjectFactory
去創建Bean1AutowiredBean2Demo
爲什麼註解DI 可以拿到之前的緩存呢?
- 因爲你已經通過空參實例化完成了
Bean1AutowiredBean2Demo
並存在beanFactoryA
中,Bean2AutowiredBean1Demo
再來拿的話就通過 getBean->getSingleton()
拿到beanFactoryA
後注入到屬性中既可完成 Bean的初始化, 而這一步是在實例化之後, 而用構造器DI 的話,Bean1AutowiredBean2Demo
實例化時會發生循環依賴.
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從單例池 (一級緩存) 中直接拿 Bean
Object singletonObject = this.singletonObjects.get(beanName);
// 一級緩存中沒有 該Bean, 並且當前 BeanName 在正在創建的 Set 集合中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從三級緩存拿 Bean, 原因: 第一次其實是拿不到的, 因爲現在只有 二級緩存中 存了一個 工廠對象, 所以成立向三級緩存添加
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
構造器 DI 方式 運行失敗的原因
構造方法DI 拋出異常前調用堆棧信息
失敗原因: 根據上述堆棧可以分析,
autowireConstructor()
試圖通過構造方法反射bean1ConstructorBean2Demo
實例時它必須先實例化bean2ConstructorBean1Demo
然後依次循環, 走到getSingleton() ->
DefaultSingletonBeanRegistry.beforeSingletonCreation()
方法時就檢測出異常, 那麼爲什麼這種方式沒有像 註解 DI 那樣解決問題呢 ?
- 註解DI方式 與 構造器DI方式最大的區別在與
AbstractAutowireCapableBeanFactory.createBeanInstance()
中的實例化策略不太一樣. 從各自定義SpringBean的源代碼上看, 構造器DI方式 需要申明當前類的構造器以及依賴的類, 而 註解DI方式則不需要 (默認空參) - 構造器DI:
return autowireConstructor(beanName, mbd, ctors, args);
使用容器的自動裝配特性, 調用匹配的構造方法進行實例化 - 註解DI:
return instantiateBean(beanName, mbd);
使用默認的無參構造方法進行實例化 - 如上圖所示, 調用堆棧 在
createBeanInstance()
實例化方法中發生了循環引用, 並沒有執行到populateBean()
進行依賴注入. 爲什麼會發生這一切?
在此之前建議閱讀一下 Spring官方對循環依賴的文檔
DI時 thorw BeanCurrentlyInCreationException 代碼片段
// 構造方法DI getSingleton() 中 thorw BeanCurrentlyInCreationException
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 當前 Bean 不存在可排除的 inCreationCheckExclusions && 當前 Bean 之前已存在於 則 thorw singletonsCurrentlyInCreation 中
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
}
構造方法DI thorw 的觸發原因: 兩次
beforeSingletonCreation()
同一個Bean, 因爲如果是沒有發生循環依賴的話接下來會執行afterSingletonCreation(beanName)
清除本輪singletonsCurrentlyInCreation.remove(beanName)
但在beforeSingletonCreation--> 遞歸 autowireConstructor() <--afterSingletonCreation
因此觸發.
// 原型BeanDI doGetBean 中 thorw BeanCurrentlyInCreationException
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 如果之前創建過相同的原型Bean 則 thorw
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
/** 創建原型模式 Bean 的實例對象*/
if (mbd.isPrototype()) {
// 原型模式 (Prototype) 每次都會創建一個新的對象
Object prototypeInstance = null;
try {
// 回調 beforePrototypeCreation() 方法, 默認的功能是註冊當前創建的原型對象
beforePrototypeCreation(beanName);
// 創建指定 Bean 的對象實例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 回調 afterPrototypeCreation() 方法, 默認的功能是告訴 IOC 容器 不再創建指定 Bean 的原型對象
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
...
}
}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
}
else {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
}
else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) {
this.prototypesCurrentlyInCreation.remove();
}
}
}
}
原型BeanDI thorw 的觸發原因: 兩次
beforePrototypeCreation()
同一個Bean, 因爲如果是沒有發生循環依賴的話接下來會執行afterPrototypeCreation(beanName)
清除本輪prototypesCurrentlyInCreation.remove()
但在beforePrototypeCreation--> 遞歸 doGetBean() <--afterPrototypeCreation
因此觸發.
回溯幾個有意思的問題
Spring是如何發現循環依賴的?
巧妙的用了LeetCode[141]中 Set 解法 把Bean 的加載順序當作一個單向鏈表邊存入邊判重.
Spring的註解DI方式 是如何解決循環依賴的 ?
因爲使用 註解DI 代碼風格上是沒有構造函數的, 在AbstractAutowireCapableBeanFactory.createBeanInstance()
走空參構造進行實例化, 所以不需要去像構造器DI 那樣去實例化別的類, 然後在 populateBean()
中進行屬性注入, 這時候已經完成實例化了要進行依賴注入了, 構造器DI方式就是在 實例化的時候翻車的呀, 具體怎麼進行循環依賴的屬性注入就靠 二 三級緩存咯.
-
一級緩存: singletonObjects 它是我們最熟悉的朋友,俗稱“單例池”“容器”,緩存創建完成單例Bean的地方, 也可以稱之爲 Spring 容器.
-
二級緩存: singletonFactories 映射創建Bean的原始工廠
-
三級緩存: earlySingletonObjects 映射Bean的早期引用,也就是說在這個Map裏的Bean不是完整的,甚至還不能稱之爲“Bean”,只是一個Instance.
千言萬語都在gif圖裏, 感謝作者vt 授權…
修復構造器 DI 引發的循環依賴的補丁有那幾種 ?
- 在主構造DI 方法上加上 @Lazy, Spring會動態代理創建代理類來解決
- 在發生循環依賴的注入主類上加上 @Autowired, 並記得刪除構造函數
- 實現 InitializingBean, ApplicationContextAware 接口, 在Spring 用 InitializingBean 時手動獲取容器注入.
- 更多
Spring Bean 是如何被創建的 ?
在學習 Spring 時你可以把它比作一家 糖果工廠 , 糖果工廠裏很多條夾心軟糖生產線, 其中最賺錢的兩條夾心軟糖生產線, 分別是A生產線 與X生產線, A生產線生產軟糖要必備一種叫@註解的糖漿原料, X 生產線則要必備另一種叫<!–Xml 的糖漿原料, 衆所周知軟糖生產有四大工藝 定位
-> 加載
-> 註冊
->DI
在這點上它倆除了 定位
, 加載
不一樣其他工藝則相同, 這兩條生產線雖然工序,原料不同但生產的夾心軟糖口感一致, 不過 A生產線軟糖包裝上選用了市面上 流行的透明塑料包裝, 透着一股子乾淨簡練, 而 X生產線 軟糖包裝則用的是白卡紙包裹的嚴嚴實實, 每次顧客拆包裝都要費點工夫, X 生產線 是廠裏的元老了, 誕生於 百糖爭鳴的 Servlet 2.0 年代, 當年它的粉絲都是一水的棒小夥, 乖小妹, 現在愛喫它的還同一批人 但都變成了老頭老太, 外加一批 軟糖考古學者, A 生產線 崛起於 和平盛世的 Servlet 3.0 年代, 它緊跟時代潮流, 潮流裏的後浪也緊跟着它, 無數青少年喫着它跨入不惑之年. 年輕人 ,你想聽我講講這兩條生產線的生產軟糖的故事嗎?
A 生產線 (AnnotationConfigApplicationContext) UML
X 生產線 (ClassPathXmlApplicationContext) UML
準備生產所需的原料
package org.springframework.context.softsweetsX;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.ZoneId;
@Component
public class SoftSweetsBean implements InitializingBean, ApplicationContextAware {
private String productionLineName;
private String dateManufacture;
public String getProductionLineName() {
return productionLineName;
}
public String getDateManufacture() {
return dateManufacture;
}
@Override
public void afterPropertiesSet() throws Exception {
this.dateManufacture = Instant.now().atZone(ZoneId.systemDefault()).toString();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.productionLineName = applicationContext.getDisplayName();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<!--SoftSweetsX.xml-->
<beans>
<bean id="softSweetsBean" class="org.springframework.context.softsweetsX.SoftSweetsBean"/>
</beans>
兩種軟糖的初體驗
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SoftSweetsTest {
@Test
public void A_ProductLine() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("org.springframework.context.softsweetsX");
context.refresh();
SoftSweetsBean bean = (SoftSweetsBean) context.getBean("softSweetsBean");
System.out.println("\n新鮮出爐的A軟糖 ~~~ \n 生產線名稱: " + bean.getProductionLineName() + "\n 生產日期: " + bean.getDateManufacture());
}
@Test
public void X_ProductLine() {
// test-resources
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/beans/factory/xml/SoftSweetsX.xml");
SoftSweetsBean bean = (SoftSweetsBean) context.getBean("softSweetsBean");
System.out.println("\n新鮮出爐的X軟糖 ~~~ \n 生產線名稱: " + bean.getProductionLineName() + "\n 生產日期: " + bean.getDateManufacture());
}
}
控制檯輸出結果
新鮮出爐的X軟糖 ~~~
生產線名稱: org.springframework.context.support.ClassPathXmlApplicationContext@525b461a
生產日期: 2020-05-28T13:57:23.738+08:00[Asia/Shanghai]
新鮮出爐的A軟糖 ~~~
生產線名稱: org.springframework.context.annotation.AnnotationConfigApplicationContext@10db82ae
生產日期: 2020-05-28T13:57:24.784+08:00[Asia/Shanghai]
如果你對生產軟糖感興趣 來吧🦃 -> SpringFamily-SourceCodeStudy
Bean 加載步驟
-
IOC (Inversion of Control 控制反轉)
- 定位 (確定原料位置)
- 加載 (找到原料後提取爲可註冊 BeanDefinition)
- 註冊 (將 BeanDefinition 校驗後註冊到 Map<String, BeanDefinition> beanDefinitionMap)
-
DI (Dependency Injection 依賴注入)
- 實例化 (反射 new 對象)ioc
- 依賴注入 (容器主動查找 bean 依賴)
IOC 控制反轉
兩條生產線的
定位
與加載
代碼
// X 生產線
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 設置容器資源加載器
super(parent);
/* <定位/> 將配置的Bean信息爲 Spring 封裝的 Resource */
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 創建 XmlBeanDefinitionReader, 即創建 Bean 讀取器
// <加載/> 並通過回調設置到容器中, 容器使用該讀取器讀取 Bean 配置資源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 爲 Bean 讀取器設置 Spring 資源加載器
// AbstractXmlApplicationContext 的祖先父類 AbstractApplicationContext 繼承 DefaultResourceLoader
// 因此容器本身也是一個資源加載器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
// 爲Bean 讀取器設置 SAX xml 解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 當Bean 讀取器讀取 Bean 定義 xml 資源文件時, 啓用 xml 的校驗機制
initBeanDefinitionReader(beanDefinitionReader);
// Bean 讀取器真正實現加載的方法
loadBeanDefinitions(beanDefinitionReader);
}
}
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
// 按照 Spring 的Bean 語義要求將 Bean 配置信息解析並轉換爲容器內部數據結構
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
/* 得到 BeanDefinitionDocumentReader 來對 XML 格式的 BeanDefinition 進行解析*/
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 獲得容器中註冊的 Bean 總數 (包含內置 bean)
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析過程的入口, 這裏使用了委派模式, BeanDefinitionDocumentReader 只是一個接口
/* <註冊/> 具體的解析過程由實現類 DefaultBeanDefinitionDocumentReader 完成 */
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 統計解析的 Bean 數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
}
}
// ----------------------------------------------- 分割線 ---------------------------------------------------
// A 生產線
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
/* <定位/> */
this.scanner.scan(basePackages);
}
}
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
public int scan(String... basePackages) {
// 獲得容器中註冊的 Bean 總數 (包含內置 bean)
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 統計解析的 Bean 數量
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
/* <加載/> */
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
/* <註冊/> */
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
}
上述代碼 表示 兩條生產線
定位
載入
是不同的, 但從 UML 類圖看 它倆 都繼承了AbstractApplicationContext
所以註冊
DI
是相同的
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
// 存儲註冊信息 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 校驗解析的 beanDefinition
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// 註冊的過程中需要線程同步, 以保證數據的一致性
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 檢查是否已經註冊過同名的 beanDefinition
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有已經註冊過的 beanDefinition 緩存
resetBeanDefinition(beanName);
}
}
}
總攬全局, 可以看到我們講的
IOC
與DI
只是衆多 Spring 生命週期中的一部分.
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 調用容器準備的刷新方法, 獲取容器的當前時間, 同時給容器設置同步標識
prepareRefresh();
// 2. <X 生產線 IOC/> 告訴子類啓動 refreshBeanFactory()方法, Bean定義資源文件的載入從子類的 refreshBeanFactory() 方法啓動
// 繼承了 AbstractRefreshableApplicationContext 的容器子類可以調用, 從 UML 圖上看 X 生產線可以,A 生產線不行
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 爲 BeanFactory 配置容器特性, 例如類加載器, 事件處理器等
prepareBeanFactory(beanFactory);
try {
// 4. 爲容器的某些子類指定的特殊的 Post 事件處理器
postProcessBeanFactory(beanFactory);
// 5. <A 生產線 IOC/> 調用所有註冊的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 爲 BeanFactory 註冊 Post 事件處理器
// BeanPostProcessor 是Bean 後置處理器, 用於監聽容器觸發的事件
registerBeanPostProcessors(beanFactory);
// 7. 初始化信息源, 和國際化相關
initMessageSource();
// 8. 初始化容器事件傳播器
initApplicationEventMulticaster();
// 9. 調用子類的某些特殊的Bean的初始化方法
onRefresh();
// 10. 爲事件傳播器註冊事件監聽器
registerListeners();
// 11. <DI/> 初始化所有剩餘的單例模式Bean (non-lazy-init)
finishBeanFactoryInitialization(beanFactory);
// 12. 初始化容器的生命週期事件處理器,併發布容器的生命週期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 13. 銷燬已經創建的單例Bean,以避免掛起資源。
destroyBeans();
// 14. 取消刷新操作, 重置容器的同步標識
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// 15. 重設公共緩存, 可能再也不需要單例bean的元數據了……
resetCommonCaches();
}
}
}
}
DI 依賴注入
循環依賴就發生在 DI 依賴注入這一步. 接下來我們詳細探討一下 它的原理. 看千遍不如手動搞一遍, 不然只是別人的知識,
依賴注入觸發規則
- 用戶第一次調用 getBean 方法時, IOC 容器觸發依賴注入
- Bean 設置爲 懶加載, 在需要預實例化 Bean 時觸發依賴注入
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
// 11. <DI/> 初始化所有剩餘的單例模式Bean (non-lazy-init)
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
beanFactory.preInstantiateSingletons();
}
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 觸發所有非惰性單例bean的實例化…
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
...
}
else {
// <實例化/> 第一次調用 getBean 方法時, IOC 容器觸發當前 Bean的依賴注入與實例化
getBean(beanName);
}
}
}
...
}
}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 根據指定的名稱獲取被管理的 Bean 名稱, 剝離指定名稱中對容器的相關依賴
// 如果指定的是別名, 將別名轉換爲 規範的 Bean 名稱
final String beanName = transformedBeanName(name);
Object bean;
// 先從緩存中讀取是否已經有被創建過的單例模式的 Bean
// 對於單例模式的Bean 整個 IOC 容器中只創建一次, 不需要重複創建
Object sharedInstance = getSingleton(beanName);
// IOC 容器創建單例模式的 Bean 示例對象
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
// 如果在容器中已有指定名稱的單例模式 Bean 被創建, 直接返回已經創建的 Bean
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 注意: FactoryBean 是創建對象的工廠 Bean, BeanFactory 是管理 Bean 的工廠
// 獲取給定 Bean 的實例對象, 主要完成 FactoryBean 的相關處理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 緩存中沒有正在創建的 單例模式的 Bean
// 緩存中已有原型模式的 Bean
// 但是由於循環依賴導致實例化對象失敗
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 對 IOC 容器中是否存在指定名稱的 BeanDefinition 進行檢查
// 首先檢查是否能對當前的 BeanFactory 中獲取所需要的 Bean,
// 如果不能則委託當前容器的父容器去查找, 如果還是找不到則沿着容器的繼承體系向父容器查找
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 解析指定 Bean 名稱的原始名稱
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// 委託父容器根據指定名稱和顯式的參數查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// 委託父容器根據指定 名稱和類型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
// 委託父容器根據指定 名稱查找
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 創建的 Bean 是否需要進行類型驗證, 一般不需要
if (!typeCheckOnly) {
// 向容器標記指定的 Bean 已經被創建
markBeanAsCreated(beanName);
}
try {
// 根據指定 Bean 名稱獲取其父級 Bean 定義
// 主要解決 Bean 繼承子類和父類公共屬性問題
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 獲取當前 Bean 所有依賴 Bean 的名稱
String[] dependsOn = mbd.getDependsOn();
/** 如果當前 Bean 有 @DependsOn 依賴的 Bean */
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 把被依賴 Bean 註冊給當前依賴的 Bean
registerDependentBean(dep, beanName);
try {
// 遞歸調用 getBean()方法, 獲取給當前依賴 Bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
/** 創建單例模式的 Bean 的實例對象*/
if (mbd.isSingleton()) {
// 這裏使用了一個匿名的內部類創建 Bean 實例對象, 並且註冊給所依賴的對象
sharedInstance = getSingleton(beanName, () -> {
try {
// 創建一個指定的 Bean 的實例對象, 如果有父級繼承, 則會合並子類和父類的定義
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 顯式地從容器中單例模式的 Bean 緩存中清除實例對象
destroySingleton(beanName);
throw ex;
}
});
// 獲取給定的 Bean 實例對象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
/** 創建原型模式 Bean 的實例對象*/
else if (mbd.isPrototype()) {
// 原型模式 (Prototype) 每次都會創建一個新的對象
Object prototypeInstance = null;
try {
// 回調 beforePrototypeCreation() 方法, 默認的功能是註冊當前創建的原型對象
beforePrototypeCreation(beanName);
// 創建指定 Bean 的對象實例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 回調 afterPrototypeCreation() 方法, 默認的功能是告訴 IOC 容器 不再創建指定 Bean 的原型對象
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
/** 創建 Web Bean 的實例對象, 比如 request , session, application 等生命週期*/
else {
// 要創建的 Bean 既不是單例模式的, 也不是原型模式的, 則根據 Bean 定義資源中
// 配置的生命週期範圍, 選擇實例化 Bean 的合適方法, 這種方式在多用於 Web 應用程序中
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
// 如果Bean 定義資源中沒有配置生命週期範圍, 則Bean定義不合法
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
// 這裏又使用了一個匿名內部類, 獲取一個指定生命週期範圍的實例
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 獲取指定 Bean 的實例對象
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
....
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
...
return (T) bean;
}
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 封裝被創建的 Bean 對象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
// 獲取實例化對象的類型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 調用 PostProcessor 後置處理器
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
...
}
mbd.postProcessed = true;
}
}
// 向容器中 緩存單例模式的 Bean 對象, 以防止循環引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 這裏是一個匿名內部類, 爲了循環引用, 儘早持有對象的引用
// 第二個參數是回調接口,實現的功能是將切面動態織入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Bean 對象的初始化, 依賴注入在此觸發
Object exposedObject = bean;
try {
// 將 Bean 實例對象封裝, 並且將 Bean 定義中配置的屬性值賦給實例對象
populateBean(beanName, mbd, instanceWrapper);
// 調用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 獲取指定名稱的已註冊的單例模式 Bean 對象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 根據名稱獲取 的已註冊的 Bean 和正在實例化的 Bean 是同一個
if (exposedObject == bean) {
// 當前實例化的 Bean 初始化完成
exposedObject = earlySingletonReference;
}
// 當前 Bean 依賴其他 Bean, 並且當發生循環引用時不允許創建新的實例對象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 獲取當前 Bean 所依賴的其他 Bean
for (String dependentBean : dependentBeans) {
// 對依賴 Bean 進行類型檢查
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
...
}
}
}
}
// 註冊完成依賴注入的 Bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
掃碼回覆 “加羣” 和我一起月入 20K+
深入淺出分享 Java 乾貨 , 找回對代碼的 Passion , 助力月入 20K+
後話
Mason 能看懂源碼,主要有兩方面在起作用,
一方面是因爲水滴石穿的不懈努力,客觀上起了作用,
一方面是因爲 Mason 通過讀毛選掌握了克服困難的方法論, 主觀上起了作用,
推薦大家參加 毛三公的B站活動 #我在讀毛選#
感謝大家的積極參與!第一期的紅軍球5.15日晚上抽獎送出第二期的讀毛 選投稿獎勵範圍:
1.學習老五篇的感想
2.結合自身情況說說對《青年運動的方向》一文的體會
期待大家新的投稿!