Spring Ioc源碼分析 之 Bean的加載(五):實例化Bean

上篇文章Spring Ioc源碼分析 之 Bean的加載(四):createBean()中咱們分析了doCreateBean()方法的大體流程,這篇文章咱們就詳細分析下實例化 beancreateBeanInstance()方法,剩下的步驟將在其餘文章中介紹。java

簡言:
實例化Bean的本質其實就是找到一個合適的構造方法,而後經過構造方法調用newInstance()來實例化Bean。
這樣看起來實例化Bean的過程很簡單,但其實Spring花費了大量經歷去尋找合適的構造方法。spring

實例化 Bean

doCreateBean()代碼 <2> 處,有一行代碼instanceWrapper = createBeanInstance(beanName, mbd, args);
這段代碼就是實例化Bean的過程。
咱們追蹤進去看一下:緩存

//AbstractAutowireCapableBeanFactory.java

//建立Bean的實例對象
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		//解析beanName 爲 class
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		//檢查確認Bean是可實例化的
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
                 //若是存在 Supplier 回調,則使用給定的回調方法初始化策略
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
                //使用 FactoryBean 的 factory-method 來建立,支持靜態工廠和實例工廠
		if (mbd.getFactoryMethodName() != null)  {
			//調用工廠方法實例化
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		//構造函數自動注入進行實例化
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
				    // 若是已緩存的解析的構造函數或者工廠方法不爲空,則能夠利用構造函數解析
			       	// 由於須要根據參數確認到底使用哪一個構造函數,該過程比較消耗性能,全部採用緩存機制
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		// 若是已經解析過,不須要再次解析
		if (resolved) {
			if (autowireNecessary) {
				//構造函數自動注入進行實例化
				//一個類有多個構造函數,每一個構造函數都有不一樣的參數,因此須要根據參數鎖定構造函數進行 bean 的實例化
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				//使用默認的無參構造方法實例化
				return instantiateBean(beanName, mbd);
			}
		}

		// Need to determine the constructor...
		//須要根據參數解析構造函數
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			//使用容器的自動裝配特性,調用匹配的構造方法實例化
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// No special handling: simply use no-arg constructor.
		//使用默認的無參構造方法實例化
		return instantiateBean(beanName, mbd);
	}
複製代碼

這段代碼中,Spring把Bean的實例話分爲了4種方式:安全

  • Supplier 回調
  • 工廠方法初始化
  • 構造函數自動注入初始化
  • 默認無參構造方法初始化

1.一、 Supplier 回調

若是存在 Supplier 回調,則調用 obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) 方法,進行初始化。
Supplier是一個接口,定義以下:bash

public interface Supplier<T> {

    T get();
    
}
複製代碼

這個接口有什麼做用?用於指定建立 bean 的回調。若是咱們設置了這樣的回調,那麼其餘的構造器或者工廠方法都會沒有用
設置的地方在BeanDefinition的構造函數中,如:app

// RootBeanDefinition.java

public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {
	super();
	setBeanClass(beanClass);
	setScope(scope);
	// 設置 instanceSupplier 屬性
	setInstanceSupplier(instanceSupplier);
}
複製代碼

1.二、工廠方法初始化

若是存在工廠方法,則使用工廠方法進行初始化。這部分代碼很是長,很複雜,這裏就不詳細說了。less

1.三、構造函數自動注入初始化

首先判斷緩存,若是緩存中存在(resolved==true),即已經解析過了,則直接使用已經解析了的。不然,先解析構造函數,而後經過構造函數自動注入初始化。ide

1.3.一、autowireConstructor()

autowireConstructor() 這個初始化方法,咱們能夠簡單理解爲經過帶有參數的構造方法,來初始化 Bean 對象。帶有參數的實例化過程至關複雜,由於存在這不肯定性,因此在判斷對應參數上作了大量工做。
代碼段以下:函數

//AbstractAutowireCapableBeanFactory.java

public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {

	// 封裝 BeanWrapperImpl 對象,並完成初始化
	BeanWrapperImpl bw = new BeanWrapperImpl();
	this.beanFactory.initBeanWrapper(bw);

	Constructor<?> constructorToUse = null;// 最終使用的構造函數
	ArgumentsHolder argsHolderToUse = null;// 構造參數
	Object[] argsToUse = null;// 構造參數

	// 判斷有無顯式指定參數,若是有則優先使用,如 xxxBeanFactory.getBean("teacher", "李華",3);
<1>	if (explicitArgs != null) {
		argsToUse = explicitArgs;
	}
	// 沒有顯式指定參數,則解析配置文件中的參數
<2>	else {
		Object[] argsToResolve = null;
		synchronized (mbd.constructorArgumentLock) {
			// 優先嚐試從緩存中獲取,spring對參數的解析過程是比較複雜也耗時的,因此這裏先嚐試從緩存中獲取已經解析過的構造函數參數
			constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
                        //若是構造方法和參數都不爲Null
			if (constructorToUse != null && mbd.constructorArgumentsResolved) {
				// Found a cached constructor...
				// 獲取緩存中的構造參數
				argsToUse = mbd.resolvedConstructorArguments;
				if (argsToUse == null) {
					argsToResolve = mbd.preparedConstructorArguments;
				}
			}
		}
		// 緩存中存在,則解析存儲在 BeanDefinition 中的參數
		// 如給定方法的構造函數 A(int ,int ),則經過此方法後就會把配置文件中的("1","1")轉換爲 (1,1)
		// 緩存中的值多是原始值也有多是最終值
		if (argsToResolve != null) {
			argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
		}
	}

	// 緩存不存在,則須要解析構造函數參數,以肯定使用哪個構造函數來進行實例化
<3>	if (constructorToUse == null) {
		// Need to resolve the constructor.
		boolean autowiring = (chosenCtors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		// 用於承載解析後的構造函數參數的值
		ConstructorArgumentValues resolvedValues = null;

		//參數個數
<4>		int minNrOfArgs;
		if (explicitArgs != null) {
			minNrOfArgs = explicitArgs.length;
		}
		else {
			// 從 BeanDefinition 中獲取構造參數,也就是從配置文件中提取構造參數
			ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
			resolvedValues = new ConstructorArgumentValues();
			// 能解析到的參數個數
			minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
		}

		// Take specified constructors, if any.
		//使用指定的構造函數,若是有的話
<5>		Constructor<?>[] candidates = chosenCtors;
		//沒有
		if (candidates == null) {
			Class<?> beanClass = mbd.getBeanClass();
			try {
				//經過反射獲取全部構造函數
				candidates = (mbd.isNonPublicAccessAllowed() ?
						beanClass.getDeclaredConstructors() : beanClass.getConstructors());
			}
			catch (Throwable ex) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Resolution of declared constructors on bean Class [" + beanClass.getName() +
						"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
			}
		}

		// 對全部構造函數進行排序,public 且 參數最多的構造函數會排在第一位
<6>		AutowireUtils.sortConstructors(candidates);
		int minTypeDiffWeight = Integer.MAX_VALUE;
		//模棱兩可的構造函數集合
		Set<Constructor<?>> ambiguousConstructors = null;
		LinkedList<UnsatisfiedDependencyException> causes = null;

		// 迭代全部構造函數,解析肯定使用哪個構造函數
<7>		for (Constructor<?> candidate : candidates) {
			// 獲取該構造函數的參數類型
<8>			Class<?>[] paramTypes = candidate.getParameterTypes();

			// 若是已經找到選用的構造函數或者須要的參數個數小於當前的構造函數參數個數,則終止。
			// 由於,已經按照參數個數降序排列了
			if (constructorToUse != null && argsToUse.length > paramTypes.length) {
				// Already found greedy constructor that can be satisfied ->
				// do not look any further, there are only less greedy constructors left.
				break;
			}
			// 參數個數不等,跳過
			if (paramTypes.length < minNrOfArgs) {
				continue;
			}

			// 參數持有者 ArgumentsHolder 對象
			ArgumentsHolder argsHolder;
<9>			if (resolvedValues != null) {
				try {
					// 獲取註解上的參數名稱 by @ConstructorProperties
					String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
					if (paramNames == null) {
						ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
						if (pnd != null) {
							// 獲取指定構造函數的參數名稱
							paramNames = pnd.getParameterNames(candidate);
						}
					}
					// 根據構造函數和構造參數,建立參數持有者 ArgumentsHolder 對象
					argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
							getUserDeclaredConstructor(candidate), autowiring);
				}
				catch (UnsatisfiedDependencyException ex) {
					if (this.beanFactory.logger.isTraceEnabled()) {
						this.beanFactory.logger.trace(
								"Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
					}
					// Swallow and try next constructor.
					if (causes == null) {
						causes = new LinkedList<>();
					}
					causes.add(ex);
					continue;
				}
			}
			else {
				// Explicit arguments given -> arguments length must match exactly.
				if (paramTypes.length != explicitArgs.length) {
					continue;
				}
				// 根據 getBean()傳入的 explicitArgs ,建立 ArgumentsHolder 對象
				argsHolder = new ArgumentsHolder(explicitArgs);
			}

			//經過構造函數參數差別值對比,得出最適合使用的構造函數
				// isLenientConstructorResolution 判斷解析構造函數的時候是否以寬鬆模式仍是嚴格模式(默認寬鬆)
				// 嚴格模式:解析構造函數時,必須全部的都須要匹配,不然拋出異常
				// 寬鬆模式:使用具備"最接近的模式"進行匹配
			int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
					argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
			// Choose this constructor if it represents the closest match.
			// 若是它表明着當前最接近的匹配則選擇其做爲構造函數
			//差別值越小,越匹配,每次和分數最小的去比較
<10>			if (typeDiffWeight < minTypeDiffWeight) {
				constructorToUse = candidate;
				argsHolderToUse = argsHolder;
				argsToUse = argsHolder.arguments;
				minTypeDiffWeight = typeDiffWeight;
				ambiguousConstructors = null;
			}
			// 若是兩個構造方法與參數值類型列表之間的差別量一致,那麼這兩個方法均可以做爲
	                // 候選項,這個時候就出現歧義了,這裏先把有歧義的構造方法放入ambiguousConstructors
			else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
				if (ambiguousConstructors == null) {
					ambiguousConstructors = new LinkedHashSet<>();
					ambiguousConstructors.add(constructorToUse);
				}
				//把候選構造函數 加入到 模棱兩可的構造函數集合中
				ambiguousConstructors.add(candidate);
			}
		}

		// 沒有可執行的構造方法,拋出異常
		if (constructorToUse == null) {
			if (causes != null) {
				UnsatisfiedDependencyException ex = causes.removeLast();
				for (Exception cause : causes) {
					this.beanFactory.onSuppressedException(cause);
				}
				throw ex;
			}
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Could not resolve matching constructor " +
					"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
		}
		//若是模棱兩可的構造函數不爲空,且爲 嚴格模式,則拋異常
		else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Ambiguous constructor matches found in bean '" + beanName + "' " +
					"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
					ambiguousConstructors);
		}
            
                 // 將解析的構造函數、參數 加入緩存
<11>		if (explicitArgs == null) {
			/* * 緩存相關信息,好比: * 1. 已解析出的構造方法對象 resolvedConstructorOrFactoryMethod * 2. 構造方法參數列表是否已解析標誌 constructorArgumentsResolved * 3. 參數值列表 resolvedConstructorArguments 或 preparedConstructorArguments * * 這些信息可用在其餘地方,用於進行快捷判斷 */
			argsHolderToUse.storeCache(mbd, constructorToUse);
		}
	}

	try {
		//獲取Bean的初始化策略
		final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
		Object beanInstance;

		//建立 Bean 對象
<12>		if (System.getSecurityManager() != null) {
			final Constructor<?> ctorToUse = constructorToUse;
			final Object[] argumentsToUse = argsToUse;
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
					beanFactory.getAccessControlContext());
		}
		else {
			beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
		}

		//設置到 bw 中
		bw.setBeanInstance(beanInstance);
		return bw;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(mbd.getResourceDescription(), beanName,
				"Bean instantiation via constructor failed", ex);
	}
	}
複製代碼

代碼很長,但不要慌,咱們來一步步分析:源碼分析

  • <1>處,判斷有無顯式指定構造參數
  • <2>處,沒有顯式指定參數,則從緩存中獲取
  • <3>處,緩存不存在,解析構造函數參數
  • <4>處,獲取構造參數個數
  • <5>處,獲取全部構造方法
  • <6>處,對全部構造方法排序
  • <7>處,遍歷全部構造方法
  • <8>處,經過參數校驗構造方法
  • <9>處,建立參數持有者 ArgumentsHolder
  • <10>處,篩選出符合的構造方法
  • <11>處,將解析的構造函數、參數 加入緩存
  • <12>處,實例化Bean對象
1.3.1.一、判斷有無顯式指定構造參數
  • explicitArgs
    外部傳入的指定構造參數
  • argsToUse
    要使用的構造參數

explicitArgs 是指外部傳入的指定構造參數,例如xxxBeanFactory.getBean("teacher", "李華",3),(李華和3)就是傳入的指定參數。
argsToUse 是咱們實例化時要使用的構造參數,這裏判斷若是explicitArgs不爲null的化,就把explicitArgs賦值給 argsToUse。

1.3.1.二、沒有顯式指定參數,則從緩存中獲取
Object[] argsToResolve = null;
	synchronized (mbd.constructorArgumentLock) {
		// 優先嚐試從緩存中獲取,spring對參數的解析過程是比較複雜也耗時的,因此這裏先嚐試從緩存中獲取已經解析過的構造函數參數
		constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
		//若是構造方法和參數都不爲Null
		if (constructorToUse != null && mbd.constructorArgumentsResolved) {
			// Found a cached constructor...
			// 獲取緩存中的構造參數
			argsToUse = mbd.resolvedConstructorArguments;
			if (argsToUse == null) {
				argsToResolve = mbd.preparedConstructorArguments;
			}
		}
	}
	// 緩存中存在,則解析存儲在 BeanDefinition 中的參數
	// 如給定方法的構造函數 A(int ,int ),則經過此方法後就會把配置文件中的("1","1")轉換爲 (1,1)
	// 緩存中的值多是原始值也有多是最終值
	if (argsToResolve != null) {
		argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
	}
複製代碼

首先從緩存中mbd.resolvedConstructorOrFactoryMethod獲取構造方法,若是緩存中存在構造方法和參數,就解析構造參數。
由於緩存中的構造參數不必定是最終值,如給定方法的構造函數 A(int ,int ),則經過此方法後就會把配置文件中的("1","1")轉換爲 (1,1)

1.3.1.三、緩存不存在,解析構造函數參數

若是緩存不存在,則須要解析構造函數參數,以肯定使用哪個構造函數來進行實例化

1.3.1.四、獲取構造參數個數
//參數個數
	int minNrOfArgs;
	if (explicitArgs != null) {
		minNrOfArgs = explicitArgs.length;
	}
	else {
		// 從 BeanDefinition 中獲取構造參數,也就是從配置文件中提取構造參數
		ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
		resolvedValues = new ConstructorArgumentValues();
		// 能解析到的參數個數
		minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
	}
複製代碼
  • 若是explicitArgs不爲null
    則直接獲取。

  • 爲null
    須要解析保存在 BeanDefinition 構造函數中指定的參數
    並獲取能解析到的參數個數

1.3.1.五、獲取全部構造方法

先嚐試獲取指定的構造方法,若是沒有,則利用反射獲取全部構造方法

1.3.1.六、對全部構造方法排序

排序的主要目的,是爲了可以更加方便的找到最匹配的構造方法,由於構造方法的確認是根據參數個數確認的。排序的規則是:先按照 public / 非 public 構造方法升序,再按照構造參數數量降序。

1.3.1.七、遍歷全部構造方法

遍歷全部構造方法,篩選出最匹配的一個

1.3.1.八、經過參數校驗構造方法
// 獲取該構造函數的參數類型
	Class<?>[] paramTypes = candidate.getParameterTypes();

	///這裏的判斷構造方法和構造方法參數 都不是空,又因爲以前對構造方法作了排序。因此在使用的參數的個數已經大於當前構造方法的參數個數的時候,實際上已經取到了想要的構造方法。
	if (constructorToUse != null && argsToUse.length > paramTypes.length) {
		// Already found greedy constructor that can be satisfied ->
		// do not look any further, there are only less greedy constructors left.
		break;
	}
	// 當前的構造參數個數小於咱們要求的個數,跳過
	if (paramTypes.length < minNrOfArgs) {
		continue;
	}
複製代碼

這段代碼也不復雜,第一個if是break分支,知足條件就跳出for循環,到這裏就意爲着找到了最匹配的構造方法。
EX: 假設如今有一組構造方法按照上面的排序規則進行排序,排序結果以下:

1. public Hello(Object, Object, Object)
     2. public Hello(Object, Object)
     3. public Hello(Object)
     4. protected Hello(Integer, Object, Object, Object)
     5. protected Hello(Integer, Object, Object)
     6. protected Hello(Integer, Object)
複製代碼

因爲是按降序排序的,因此會先去匹配構造方法1,發現 argsToUse.length > paramTypes.length

第二個if是快速判斷當前構造方法是否符合咱們的要求。

  • paramTypes
    當前構造方法的參數個數
  • minNrOfArgs
    咱們要求的構造方法的參數個數 若是當前的構造參數個數小於咱們要求的個數,說明當前構造方法不符合咱們的要求,直接 continue
1.3.1.九、建立參數持有者 ArgumentsHolder
// 參數持有者 ArgumentsHolder 對象
	ArgumentsHolder argsHolder;
	if (resolvedValues != null) {
		try {
			// 獲取註解上的參數名稱 by @ConstructorProperties
			String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
			if (paramNames == null) {
				// ParameterNameDiscoverer 是用於解析方法和構造函數的參數名稱的接口,爲參數名稱探測器
				ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
				if (pnd != null) {
					// 獲取指定構造函數的參數名稱
					paramNames = pnd.getParameterNames(candidate);
				}
			}
			// 根據構造函數和構造參數,建立參數持有者 ArgumentsHolder 對象
			argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
					getUserDeclaredConstructor(candidate), autowiring);
		}
		catch (UnsatisfiedDependencyException ex) {
			if (this.beanFactory.logger.isTraceEnabled()) {
				this.beanFactory.logger.trace(
						"Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
			}
			// Swallow and try next constructor.
			if (causes == null) {
				causes = new LinkedList<>();
			}
			causes.add(ex);
			continue;
		}
	}
	else {
		// Explicit arguments given -> arguments length must match exactly.
		if (paramTypes.length != explicitArgs.length) {
			continue;
		}
		// 根據 getBean()傳入的 explicitArgs ,建立 ArgumentsHolder 對象
		argsHolder = new ArgumentsHolder(explicitArgs);
	}
複製代碼

這裏主要有兩個邏輯:

  • resolvedValues != null
    即沒有顯示指定構造參數
  • resolvedValues == null
    即顯示指定了構造參數

第一個分支:
先經過@ConstructorProperties註解獲取構造參數名稱,若是獲取不到,再經過ParameterNameDiscoverer獲取,最後建立 ArgumentsHolder
第二個分支:
直接使用顯示傳入的構造參數 explicitArgs 來 new 一個ArgumentsHolder

將參數包裝成 ArgumentsHolder 對象。該對象用於保存參數,咱們稱之爲參數持有者。在這個過程當中再次解析構造參數,進行類型轉換,如把配置文件中的string轉換成須要的int。
當將對象包裝成 ArgumentsHolder 對象後,咱們就能夠經過它來進行構造函數匹配。匹配分爲嚴格模式和寬鬆模式:

  • 嚴格模式:解析構造函數時,必須全部參數都須要匹配,不然拋出異常。

  • 寬鬆模式:從模棱兩可的構造方法中,選擇最接近的。
    判斷的依據是根據 BeanDefinition 的 isLenientConstructorResolution 屬性(該參數是咱們在構造 AbstractBeanDefinition 對象是傳遞的)來獲取類型差別權重(typeDiffWeight) 的。

1.3.1.十、篩選出符合的構造方法
//經過構造函數參數差別值對比,得出最適合使用的構造函數
		// isLenientConstructorResolution 判斷解析構造函數的時候是否以寬鬆模式仍是嚴格模式(默認寬鬆)
		// 嚴格模式:解析構造函數時,必須全部的都須要匹配,不然拋出異常
		// 寬鬆模式:使用具備"最接近的模式"進行匹配
	int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
			argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
	// Choose this constructor if it represents the closest match.
	// 若是它表明着當前最接近的匹配則選擇其做爲構造函數
	//差別值越小,越匹配,每次和分數最小的去比較
	if (typeDiffWeight < minTypeDiffWeight) {
		constructorToUse = candidate;
		argsHolderToUse = argsHolder;
		argsToUse = argsHolder.arguments;
		minTypeDiffWeight = typeDiffWeight;
		ambiguousConstructors = null;
	}
	// 若是兩個構造方法與參數值類型列表之間的差別量一致,那麼這兩個方法均可以做爲
	// 候選項,這個時候就出現歧義了,這裏先把有歧義的構造方法放入ambiguousConstructors
	else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
		if (ambiguousConstructors == null) {
			ambiguousConstructors = new LinkedHashSet<>();
			ambiguousConstructors.add(constructorToUse);
		}
		//把候選構造函數 加入到 模棱兩可的構造函數集合中
		ambiguousConstructors.add(candidate);
	}


/ 沒有可執行的構造方法,拋出異常
if (constructorToUse == null) {
	if (causes != null) {
		UnsatisfiedDependencyException ex = causes.removeLast();
		for (Exception cause : causes) {
			this.beanFactory.onSuppressedException(cause);
		}
		throw ex;
	}
	throw new BeanCreationException(mbd.getResourceDescription(), beanName,
			"Could not resolve matching constructor " +
			"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");

/若是模棱兩可的構造函數不爲空,且爲 嚴格模式,則拋異常
else if (ambiguousConstructors != null && mbd.isLenientConstructorResolution()) {
	throw new BeanCreationException(mbd.getResourceDescription(), beanName,
			"Ambiguous constructor matches found in bean '" + beanName + "' " +
			"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
			ambiguousConstructors);
			}
複製代碼

先經過計算得出當前構造方法的差別值typeDiffWeight,每次和分數最小的去比較,篩選出差別值最小的,最終比較出一個最匹配的構造方法。
差別值大於最小差別值的,加入到候選集合ambiguousConstructors,我稱之爲模棱兩可的構造方法,該集合在《寬鬆模式》下使用。

至此,全部構造方法都遍歷完畢。若是仍沒有篩選出構造方法,拋出異常。
若是模棱兩可的構造方法不爲空,但模式爲 嚴格模式,則拋異常。

1.3.1.十一、將解析的構造函數、參數 加入緩存
// 將解析的構造函數、參數 加入緩存
if (explicitArgs == null) {
	/* * 緩存相關信息,好比: * 1. 已解析出的構造方法對象 resolvedConstructorOrFactoryMethod * 2. 構造方法參數列表是否已解析標誌 constructorArgumentsResolved * 3. 參數值列表 resolvedConstructorArguments 或 preparedConstructorArguments * * 這些信息可用在其餘地方,用於進行快捷判斷 */
	argsHolderToUse.storeCache(mbd, constructorToUse);
	}
複製代碼

繼續追蹤:

// ArgumentsHolder.java
        public final Object rawArguments[];

	public final Object arguments[];

	public final Object preparedArguments[];

public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {
	synchronized (mbd.constructorArgumentLock) {
		mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;
		mbd.constructorArgumentsResolved = true;
		if (this.resolveNecessary) {
			mbd.preparedConstructorArguments = this.preparedArguments;
		}
		else {
			mbd.resolvedConstructorArguments = this.arguments;
		}
	}
		}
複製代碼

相信你們看到這裏應該對resolvedConstructorOrFactoryMethodresolvedConstructorArguments等這幾個參數很熟悉。
正如你所想,在前面判斷緩存中是否存在的時候,就是經過這幾個參數來判斷的。

1.3.1.十二、實例化Bean對象

strategy.instantiate

//SimpleInstantiationStrategy.java

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, final Constructor<?> ctor, @Nullable Object... args) {
	// 沒有方法覆蓋,直接使用反射實例化便可
	if (!bd.hasMethodOverrides()) {
		if (System.getSecurityManager() != null) {
			// use own privileged to change accessibility (when security is on)
			// 設置構造方法,可訪問
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				ReflectionUtils.makeAccessible(ctor);
				return null;
			});
		}
		// 經過 BeanUtils 直接使用構造函數實例化 Bean 對象
		return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));
	}
	else {
		// 使用 CGLIB 建立代理對象
		//方法覆蓋,在調用目標方法的時候,對調用過程進行攔截,調用實現加強功能的攔截器,返回原來實例的代理
		//因此要用cglib動態代理
		return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
	}
	}
複製代碼

BeanUtils.instantiateClass(ctor, args)

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		Assert.notNull(ctor, "Constructor must not be null");
		try {
			// 設置構造方法,可訪問
			ReflectionUtils.makeAccessible(ctor);
			// 使用構造方法,建立對象 newInstance
			return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
					KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
		}
		catch (InstantiationException ex) {
			throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
		}
		catch (IllegalAccessException ex) {
			throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
		}
		catch (IllegalArgumentException ex) {
			throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
		}
		catch (InvocationTargetException ex) {
			throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
		}
	}
複製代碼

1.3.二、圖解流程

由於這段代碼仍是挺複雜的,因此我畫了一個(explicitArgs=null)的分支流程圖,便於理解。

1.三、默認無參構造方法初始化

通過有參構造方法初始化源碼的摧殘以後,再來看無參的源碼,會發現簡單多了。

return instantiateBean(beanName, mbd);
複製代碼

繼續追蹤:

//使用默認的無參構造方法實例化Bean對象
	protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	try {
		Object beanInstance;
		final BeanFactory parent = this;
		//獲取系統的安全管理接口,JDK標準的安全管理API
		if (System.getSecurityManager() != null) {
			//這裏是一個匿名內置類,根據實例化策略建立實例對象
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					getInstantiationStrategy().instantiate(mbd, beanName, parent),
					getAccessControlContext());
		}
		else {
			//將實例化的對象封裝起來
			beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
		}
		BeanWrapper bw = new BeanWrapperImpl(beanInstance);
		initBeanWrapper(bw);
		return bw;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
	}
	}
複製代碼

看過有參構造方法初始化的源碼以後,再看看無參的,發現代碼真的簡單太多了,沒有複雜的肯定構造參數、構造方法的邏輯。

instantiate(mbd, beanName, parent)

//SimpleInstantiationStrategy.java

//使用初始化策略實例化Bean對象
	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
	// Don't override the class with CGLIB if no overrides.
	// 沒有覆蓋,直接使用反射實例化便可
	if (!bd.hasMethodOverrides()) {
		Constructor<?> constructorToUse;
		synchronized (bd.constructorArgumentLock) {
			//從緩存中獲取對象的構造方法或工廠方法
			constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
			//緩存沒有
			if (constructorToUse == null) {
				//使用JDK的反射機制,判斷要實例化的Bean是不是接口
				final Class<?> clazz = bd.getBeanClass();
				if (clazz.isInterface()) {
					throw new BeanInstantiationException(clazz, "Specified class is an interface");
				}
				try {
					if (System.getSecurityManager() != null) {
						//這裏是一個匿名內置類,使用反射機制獲取Bean的構造方法
						constructorToUse = AccessController.doPrivileged(
								(PrivilegedExceptionAction<Constructor<?>>) () -> clazz.getDeclaredConstructor());
					}
					else {
						constructorToUse =	clazz.getDeclaredConstructor();
					}
					bd.resolvedConstructorOrFactoryMethod = constructorToUse;
				}
				catch (Throwable ex) {
					throw new BeanInstantiationException(clazz, "No default constructor found", ex);
				}
			}
		}
		//使用BeanUtils實例化,經過反射機制調用」構造方法.newInstance(arg)」來進行實例化
		return BeanUtils.instantiateClass(constructorToUse);
	}
	else {
		// Must generate CGLIB subclass.
		//有方法覆蓋,使用CGLIB來實例化對象
		//方法覆蓋,在調用目標方法的時候,對調用過程進行攔截,調用實現加強功能的攔截器,返回原來實例的代理
		//因此要用cglib動態代理
		return instantiateWithMethodInjection(bd, beanName, owner);
	}
	}
複製代碼

很簡單的幾個步驟:

  • 判斷有無方法覆蓋
  • 嘗試從緩存中獲取構造方法
  • 校驗bean是否爲interface
  • 利用反射獲取默認構造方法
  • 利用BeanUtils實例化

BeanUtils.instantiateClass(constructorToUse)

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
	Assert.notNull(ctor, "Constructor must not be null");
	try {
		// 設置構造方法,可訪問
		ReflectionUtils.makeAccessible(ctor);
		// 使用構造方法,建立對象 newInstance
		return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
				KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
	}
	catch (InstantiationException ex) {
		throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
	}
	catch (IllegalAccessException ex) {
		throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
	}
	catch (IllegalArgumentException ex) {
		throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
	}
	catch (InvocationTargetException ex) {
		throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
	}
	}
複製代碼

先設置強吻訪問,而後newInstance()建立對象。

總結
複製代碼

對於 createBeanInstance() 方法而言,他就是選擇合適實例化策略來爲 bean 建立實例對象,具體的策略有:

  • Supplier 回調方式

  • 工廠方法初始化

  • 構造函數自動注入初始化

  • 默認構造函數注入。

其中,工廠方法初始化和構造函數自動注入初始化兩種方式最爲複雜,主要是由於構造函數和構造參數的不肯定性,Spring 須要花大量的精力來肯定構造函數和構造參數,若是肯定了則好辦,直接選擇實例化策略便可。

固然,在實例化的時候會根據是否有須要覆蓋或者動態替換掉的方法,由於存在覆蓋或者織入的話須要建立動態代理將方法織入,這個時候就只能選擇 CGLIB 的方式來實例化,不然直接利用反射的方式便可,方便快捷。

最後:
到這裏實例化Bean的代碼就分析完了,這部分源碼看起來仍是有難度的,看的我頭髮的慌了,寫的也挺累的,但仍是會繼續寫下去,會發現當你研究懂一段源碼以後,那種成就感真的很爽0.0
下篇將分析第4個過程:循環依賴的處理。其實,循環依賴並不只僅只是在 #doCreateBean(...) 方法中處理,在整個加載 bean 的過程當中都有涉及。因此下篇內容並不只僅只侷限於 #doCreateBean(...) 方法。

PS: 碼字不易,但願你們多多點贊哈,給小弟點動力T.T

參考:公衆號-芋道源碼

相關文章
相關標籤/搜索