悶棍暴打面試官 Spring源碼系列: (一) Spring 如何解決循環依賴

前言

初夏時節, 時間: 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 中的循環依賴也是同理!"

DD出行

見微知著

我們暫不討論 Spring 的循環依賴, 先看一道 LeetCode 題目 141. 環形鏈表
擴展: 在多線程環境下使用JDK1.7中的HashMap, 併發調用resize()時會出現環形鏈表,後再get()會導致CPU 100%, 那我們該如何去判斷環形鏈表呢?

給定一個鏈表,判斷鏈表中是否有環。

爲了表示給定鏈表中的環,我們使用整數 pos
來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是
-1,則在該鏈表中沒有環。

示例 1:

輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。

示例 1

示例 2:

輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。

示例 2

示例 3:

輸入:head = [1], pos = -1
輸出:false
解釋:鏈表中沒有環。

示例 3

那麼 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

LeetCode 漸進式題解庫: 讓天下沒有難刷的題 (Java)

LeetCode 漸進式題解庫: 刷題使我快樂,自律使我自由!

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 類中含有屬性 Bean2AutowiredBean1DemoBean2AutowiredBean1Demo 類中含有屬性 Bean1AutowiredBean2Demo,這兩個類在初始化的時候經歷了以下的步驟:

  1. 創建 Bean1AutowiredBean2Demo,先記錄對應的 beanName 然後將 Bean1AutowiredBean2Demo創建工廠 beanFactoryA 放入緩存中
  2. Bean1AutowiredBean2Demo的屬性填充方法 populateBean,檢查到依賴 Bean2AutowiredBean1Demo,緩存中沒有 Bean2AutowiredBean1Demo 的實例或者單例緩存,於是要去實例化 Bean2AutowiredBean1Demo
  3. 開始實例化 Bean2AutowiredBean1Demo,經歷創建 Bean2AutowiredBean1Demo的過程,到了屬性填充方法,檢查到依賴了 Bean1AutowiredBean2Demo
  4. 調用 getBean(Bean1AutowiredBean2Demo) 方法,在這個函數中,不是真正去實例化 Bean1AutowiredBean2Demo,而是先去檢測緩存中是否有已經創建好的對應的 bean,或者已經創建好的 beanFactory
  5. 檢測到 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

AnnotationConfigApplicationContext UML類圖

X 生產線 (ClassPathXmlApplicationContext) UML

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 控制反轉

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);
		}
	}
}    

總攬全局, 可以看到我們講的 IOCDI 只是衆多 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

循環依賴就發生在 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日晚上抽獎送出[OK]第二期的讀毛 選投稿獎勵範圍:
1.學習老五篇的感想
2.結合自身情況說說對《青年運動的方向》一文的體會
期待大家新的投稿!

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章