Spring Ioc源碼分析系列--實例化Bean的幾種方法 前言 前面的文章Spring Ioc源碼分析系列--Bean實例化過程(二)在講解到bean真正通過那些方式實例化出來的時候,並沒有繼續分析了,而是留到了這裡去分析,主要是因為獲取獲取構造函數,推斷構造函數也是一個比較複雜的操作,就想另起 ...
Spring Ioc源碼分析系列--實例化Bean的幾種方法
前言
前面的文章Spring Ioc源碼分析系列--Bean實例化過程(二)在講解到bean真正通過那些方式實例化出來的時候,並沒有繼續分析了,而是留到了這裡去分析,主要是因為獲取獲取構造函數,推斷構造函數也是一個比較複雜的操作,就想另起一篇文章再說,但是總的來說,應該不會比前面的邏輯繞,因為這裡很清晰,就是實例化對象的幾種方法,那麼實例化對象有哪幾種選擇呢?沒印象,那說明前面的文章沒留下影響,回去翻翻。所以廢話少說,跟著上面文章的口子,我們來分析實例化bean的過程。
源碼分析
首先,這裡回憶一下之前說到什麼。
本篇文章的核心邏輯入口是在AbstractAutowireCapableBeanFactory#createBeanInstance()
方法里。
跟進createBeanInstance(beanName, mbd, args)
方法。這個方法幹了哪幾件事?
- 首先嘗試調用
obtainFromSupplier()
實例化bean - 嘗試調用
instantiateUsingFactoryMethod()
實例化bean - 根據給定參數推斷構造函數實例化bean
- 以上均無,則使用預設構造函數實例化bean
先貼一下代碼,然後逐個分析。
/**
* Create a new instance for the specified bean, using an appropriate instantiation strategy:
* factory method, constructor autowiring, or simple instantiation.
*
* 使用適當的實例化策略為指定的 bean 創建一個新實例:工廠方法、構造函數自動裝配或簡單實例化。
*
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a BeanWrapper for the new instance
* @see #obtainFromSupplier
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
// 確保此時實際解析了 bean 類。
Class<?> beanClass = resolveBeanClass(mbd, beanName);
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());
}
// 通過bd中提供的instanceSupplier來獲取一個對象
// 正常bd中都不會有這個instanceSupplier屬性,這裡也是Spring提供的一個擴展點,但實際上不常用
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
//如果工廠方法不為null,則使用工廠方法初始化策略
// bd中提供了factoryMethodName屬性,那麼要使用工廠方法的方式來創建對象,
// 工廠方法又會區分靜態工廠方法跟實例工廠方法
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 在原型模式下,如果已經創建過一次這個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) {
//構造函數自動註入
return autowireConstructor(beanName, mbd, null, null);
}
else {
//使用預設構造函數進行構造
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
//需要根據參數解析構造函數
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
//構造函數自動註入
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
// 預設構造的首選構造函數?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
//使用預設構造函數
return instantiateBean(beanName, mbd);
}
使用Supplier實例化bean
這一塊的邏輯對應如下
// 通過bd中提供的instanceSupplier來獲取一個對象
// 正常bd中都不會有這個instanceSupplier屬性,這裡也是Spring提供的一個擴展點,但實際上不常用
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
Supplier在Spring裡面的使用是相對比較新的,看了一個代碼的commit記錄,這一塊的代碼是在2016年12月19號加上的,相對來說還是比較新的。
Supplier就是一個Java 8提供的函數式編程介面,裡面提供一個get()
方法,可以通過這個方法實例化bean對象。
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
跟進obtainFromSupplier()
方法,可以看到邏輯是比較簡單的,通過一個NamedThreadLocal
設置依賴關係beanName
,然後調用instanceSupplier.get()
獲取對象,隨後包裝成一個BeanWrapper
返回。
/**
* Obtain a bean instance from the given supplier.
*
* 從給定的 supplier 處獲取一個 bean 實例。
*
* @param instanceSupplier the configured supplier
* @param beanName the corresponding bean name
* @return a BeanWrapper for the new instance
* @since 5.0
* @see #getObjectForBeanInstance
*/
protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
Object instance;
// 這裡是處理 Supplier 創建的 bean 的內外部名稱依賴關係
String outerBean = this.currentlyCreatedBean.get();
this.currentlyCreatedBean.set(beanName);
try {
// 調用 get() 方法獲取對象
instance = instanceSupplier.get();
}
finally {
if (outerBean != null) {
this.currentlyCreatedBean.set(outerBean);
}
else {
this.currentlyCreatedBean.remove();
}
}
if (instance == null) {
instance = new NullBean();
}
BeanWrapper bw = new BeanWrapperImpl(instance);
initBeanWrapper(bw);
return bw;
}
使用工廠方法實例化bean
簡介
這一塊的代碼邏輯對應如下
//如果工廠方法不為null,則使用工廠方法初始化策略
// bd中提供了factoryMethodName屬性,那麼要使用工廠方法的方式來創建對象,
// 工廠方法又會區分靜態工廠方法跟實例工廠方法
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
跟進instantiateUsingFactoryMethod()
方法,可以看到這裡先new
了一個ConstructorResolver
,這個ConstructorResolver
非常重要,它會去推斷合適的構造函數。實例化bean另起這篇文章來寫,很大程度就是因為這個ConstructorResolver
,我想寫清楚一點。
/**
* Instantiate the bean using a named factory method. The method may be static, if the
* mbd parameter specifies a class, rather than a factoryBean, or an instance variable
* on a factory object itself configured using Dependency Injection.
*
* 使用命名工廠方法實例化 bean。
* 如果 mbd 參數指定一個類,而不是 factoryBean,或者使用依賴註入配置的工廠對象本身的實例變數,則該方法可能是靜態的。
*
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (-> use constructor argument values from bean definition)
* @return a BeanWrapper for the new instance
* @see #getBean(String, Object[])
*/
protected BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
首先來看一下ConstructorResolver
類上的定義,翻譯一下就是:用於解析構造函數和工廠方法的委托,通過參數匹配執行構造函數解析。
Delegate for resolving constructors and factory methods.Performs constructor resolution through argument matching.
這個類的作用非常強大,可以解析實例化對象的所需的構造器,如果有多個,會根據構造器的參數類型和給定的參數類型通過計算權重的方式去匹配一個最佳的構造器。
跟進方法instantiateUsingFactoryMethod()
代碼,這個方法整整三百行,可以說非常離譜。
這裡先貼個圖,看下基本的邏輯。其實邏輯也跟我們寫業務差不多,先做些基礎準備,這裡就是初始化個BeanWrapperImpl
,給工廠bean屬性賦賦值等,然後就去緩存去工廠方法和參數,取不到就去解析獲取方法和參數,然後利用工廠方法和參數,實例化一個對象返回,邏輯清晰。
下麵分段來看一下代碼吧。
基礎屬性賦值
這一塊代碼比較簡單,就是初始化一個BeanWrapperImpl
,然後給一些屬性賦值。
// 創建並初始化一個 BeanWrapperImpl
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
// 實例化這個Bean的工廠Bean
Object factoryBean;
// 工廠Bean的Class
Class<?> factoryClass;
// 靜態工廠方法或者是實例化工廠方法
boolean isStatic;
/*下麵這段代碼就是為上面申明的這三個屬性賦值*/
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
// 如果創建這個Bean的工廠就是這個Bean本身的話,那麼直接拋出異常
if (factoryBeanName.equals(beanName)) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"factory-bean reference points back to the same bean definition");
}
// 得到創建這個Bean的工廠Bean
factoryBean = this.beanFactory.getBean(factoryBeanName);
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
throw new ImplicitlyAppearedSingletonException();
}
factoryClass = factoryBean.getClass();
isStatic = false;
}
else {
// It's a static factory method on the bean class.
// factoryBeanName為null,說明是通過靜態工廠方法來實例化Bean的
// 靜態工廠進行實例化Bean,beanClass屬性必須要是工廠的class,如果為空,直接報錯
if (!mbd.hasBeanClass()) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"bean definition declares neither a bean class nor a factory-bean reference");
}
factoryBean = null;
factoryClass = mbd.getBeanClass();
isStatic = true;
}
從緩存獲取參數
完成準備後,接下來是從緩存中獲取工廠方法和參數,這裡也比較簡單,跟著註釋看看。這裡需要註意resolvedConstructorArguments
和preparedConstructorArguments
這兩個參數緩存,resolvedConstructorArguments
用於緩存完全解析的構造函數參數的包可見欄位,preparedConstructorArguments
用於緩存部分準備好的構造函數參數的包可見欄位。其中如果是preparedConstructorArguments
中存在參數的話,需要調用resolvePreparedArguments()
方法再次進行解析。
// 到這裡已經得到了一個BeanWrapper,明確了實例化當前這個Bean到底是靜態工廠還是實例工廠
// 並且已經確定了工廠Bean
//================//
// 最終確定的要用來創建對象的方法
Method factoryMethodToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 從緩存中解析獲取參數
// 參數分析時已經說過,explicitArgs就是null
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
// 下麵這段代碼是什麼意思呢?
// 在原型模式下,我們會多次創建一個Bean,所以Spring對參數以及所使用的方法做了緩存
// 在第二次創建原型對象的時候會進入這段緩存的邏輯
// 但是這裡有個問題,為什麼Spring對參數有兩個緩存呢?
// 一:resolvedConstructorArguments 用於緩存完全解析的構造函數參數的包可見欄位
// 二:preparedConstructorArguments 用於緩存部分準備好的構造函數參數的包可見欄位
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
// 緩存已經解析過的工廠方法或者構造方法
if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached factory method...
// 找到一個緩存的工廠方法...resolvedConstructorArguments 跟 preparedConstructorArguments都是對參數的緩存
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
// preparedConstructorArguments需要再次進行解析,其中主要完成了獲取依賴以及類型轉換等工作
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
}
}
跟進resolvePreparedArguments()
方法,簡單來說就是根據參數的類型去解析獲取出需要的參數,同時這裡也會完成類型轉換。
/**
* Resolve the prepared arguments stored in the given bean definition.
*
* 解析存儲在給定 bean 定義中的準備好的參數。
*
*/
private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
Executable executable, Object[] argsToResolve, boolean fallback) {
// 獲取類型轉換器
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
// 獲取占位符解析器
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
Class<?>[] paramTypes = executable.getParameterTypes();
Object[] resolvedArgs = new Object[argsToResolve.length];
// 逐個遍歷參數
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
Object argValue = argsToResolve[argIndex];
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
// 如果是自動裝配標誌,則進行依賴解析
if (argValue == autowiredArgumentMarker) {
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, fallback);
}
// 如果是 BeanMetadataElement 類型,則進行各種BeanDefinition的解析,因為 BeanMetadataElement 的實現基本上是各種BeanMetadataElement
else if (argValue instanceof BeanMetadataElement) {
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
}
// String 類型
else if (argValue instanceof String) {
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
}
Class<?> paramType = paramTypes[argIndex];
try {
// 進行類型轉換
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
}
return resolvedArgs;
}
解析獲取工廠方法和參數
解析一下這段邏輯,這裡會先去獲取該工廠bean的所有方法作為候選方法,然後按照先前賦值的屬性進行簡單篩選。如果非常巧合,只找到了一個,同時參數又為空,這時候就可以利用這個唯一的方法去實例化對象,省略了後面參數解析的複雜邏輯。如果不巧,那就只好老老實實去解析參數匹配。
- 首先這裡會對所有方法進行排序,這裡會對給定的工廠方法進行排序,優先選擇公共方法和具有最多參數的“貪婪”方法。結果將首先包含公共方法,參數數量減少,然後是非公共方法,參數數量再次減少。
- 然後會調用
resolveConstructorArguments()
去獲取可執行方法的最少參數個數minNrOfArgs
。 - 隨後遍歷所有的候選方法,方法的參數個數必須滿足大於或等於可執行方法的最少參數個數
minNrOfArgs
,嘗試通過方法參數名稱,方法參數類型和給定的resolvedValues
屬性裡面去查找或者通過自動註入獲取到參數值,然後封裝成ArgumentsHolder
對象。 - 如果存在多個,通過權重計算獲取最合適的工廠方法。如果最後沒有找到工廠方法和參數,直接報錯。如果最合適的有多個,那麼也直接報錯。
- 最後對工廠方法和參數進行緩存,然後調用
instantiate()
方法,使用前面解析出來的工廠方法和參數進行對象創建。
具體實現如下,可以跟著註釋查看。這裡就不全部再去解析,下麵就簡單分析一下確定方法執行所需的最小參數和封裝ArgumentsHolder
對象的邏輯。
// 緩存中找不到方法獲取參數,執行到這段代碼說明是第一次實例化這個對象
if (factoryMethodToUse == null || argsToUse == null) {
// Need to determine the factory method...
// Try all methods with this name to see if they match the given arguments.
// 需要確定工廠方法...嘗試所有具有此名稱的方法,看看它們是否與給定的參數匹配。
// 如果被cglib代理的話,獲取父類的class
factoryClass = ClassUtils.getUserClass(factoryClass);
// 獲取到工廠類中的所有方法
List<Method> candidateList = null;
if (mbd.isFactoryMethodUnique) {
if (factoryMethodToUse == null) {
factoryMethodToUse = mbd.getResolvedFactoryMethod();
}
if (factoryMethodToUse != null) {
candidateList = Collections.singletonList(factoryMethodToUse);
}
}
if (candidateList == null) {
candidateList = new ArrayList<>();
// 獲取到工廠類中的所有方法,接下來要一步步從這些方法中篩選出來符合要求的方法
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
// 第一步篩選:之前 在第二段代碼中已經推斷了方法是靜態或者非靜態的
// 所以這裡第一個要求就是要滿足靜態/非靜態這個條件
// 第二個要求就是必須符合bd中定義的factoryMethodName的名稱
// 其中第二個要求請註意,如果bd是一個configurationClassBeanDefinition,
// 也就是說是通過掃描@Bean註解產生的,那麼在判斷時還會添加是否標註了@Bean註解
for (Method candidate : rawCandidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
candidateList.add(candidate);
}
}
}
// 如果只有一個,則沒有重載方法,不需要查找,這裡就會省略後續複雜的推斷了,可以直接確定方法
if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Method uniqueCandidate = candidateList.get(0);
// 如果沒有參數
if (uniqueCandidate.getParameterCount() == 0) {
mbd.factoryMethodToIntrospect = uniqueCandidate;
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
// 將之前得到的方法集合轉換成數組
// 到這一步得到的其實就是某一個方法的所有重載方法
// 比如 codegitz(),codegitz(String name),codegitz(String name,int age)
Method[] candidates = candidateList.toArray(new Method[0]);
// 排序,public跟參數多的優先順序越高
AutowireUtils.sortFactoryMethods(candidates);
// 用來保存從配置文件中解析出來的參數
ConstructorArgumentValues resolvedValues = null;
// 是否使用了自動註入,本段代碼中沒有使用到這個屬性,但是在後面用到了
boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
int minTypeDiffWeight = Integer.MAX_VALUE;
// 可能出現多個符合要求的方法,用這個集合保存,實際上如果這個集合有值,就會拋出異常了
Set<Method> ambiguousFactoryMethods = null;
int minNrOfArgs;
// 必定為null,不考慮了
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
// We don't have arguments passed in programmatically, so we need to resolve the
// arguments specified in the constructor arguments held in the bean definition.
// 就是說配置文件中指定了要使用的參數,那麼需要對其進行解析,解析後的值就存儲在resolvedValues這個集合中
if (mbd.hasConstructorArgumentValues()) {
// 通過解析constructor-arg標簽,將參數封裝成了ConstructorArgumentValues
// ConstructorArgumentValues這個類在下文我們專門分析
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
// 解析標簽中的屬性,類似進行類型轉換,後文進行詳細分析
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
else {
// 配置文件中沒有指定要使用的參數,所以執行方法的最小參數個數就是0
minNrOfArgs = 0;
}
}
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Method candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
if (paramTypes.length >= minNrOfArgs) {
ArgumentsHolder argsHolder;
if (explicitArgs != null) {
// Explicit arguments given -> arguments length must match exactly.
// 給定的顯式參數 -> 參數長度必須完全匹配。
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
else {
// Resolved constructor arguments: type conversion and/or autowiring necessary.
// 已解決的構造函數參數:需要類型轉換和自動裝配。
try {
String[] paramNames = null;
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
// 給定解析的構造函數參數值,創建一個參數數組以調用構造函數或工廠方法
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
}
// Swallow and try next overloaded factory method.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
}
// 計算給定參數和方法定義參數的權重,選擇一個最合適的方法
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this factory method if it represents the closest match.
// 如果它代表最接近的匹配,則選擇此工廠方法。
if (typeDiffWeight < minTypeDiffWeight) {
factoryMethodToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousFactoryMethods = null;
}
// Find out about ambiguity: In case of the same type difference weight
// for methods with the same number of parameters, collect such candidates
// and eventually raise an ambiguity exception.
// However, only perform that check in non-lenient constructor resolution mode,
// and explicitly ignore overridden methods (with the same parameter signature).
// 找出歧義:如果具有相同數量的參數的方法的類型差異權重相同,則收集此類候選並最終引發歧義異常。
// 但是,僅在非寬鬆構造函數解析模式下執行該檢查,並顯式忽略重寫的方法(具有相同的參數簽名)
// 可以理解為,這裡就是收集參數類型和數量一樣,方法名一樣,如果存在這種情況,最終會拋出異常
// 為啥會出現這種情況,我理解可能是同名方法參數的順序不一樣導致的,例如 sayHi(String name,String age) 和 sayHi(String age,String name)
else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
!mbd.isLenientConstructorResolution() &&
paramTypes.length == factoryMethodToUse.getParameterCount() &&
!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
if (ambiguousFactoryMethods == null) {
ambiguousFactoryMethods = new LinkedHashSet<>();
ambiguousFactoryMethods.add(factoryMethodToUse);
}
ambiguousFactoryMethods.add(candidate);
}
}
}
// 最終沒有找到可用的工廠方法或者參數,或者有多個符合要求的方法等情況,進行異常處理
if (factoryMethodToUse == null || argsToUse == null) {
// 省略一些異常處理...
}
// 緩存參數
if (explicitArgs == null && argsHolderToUse != null) {
mbd.factoryMethodToIntrospect = factoryMethodToUse;
argsHolderToUse.storeCache(mbd, factoryMethodToUse);
}
}
簡單的邏輯就不做過多解釋了,這裡重點來看下是怎麼確定方法最小使用參數個數的。跟進代碼resolveConstructorArguments()
,該方法會先獲取constructor-arg
標簽指定的參數個數,然後去獲取參數的下標值,如果下標值更大,那麼最小的參數個數就取下標值加一。
/**
* Resolve the constructor arguments for this bean into the resolvedValues object.
* This may involve looking up other beans.
* <p>This method is also used for handling invocations of static factory methods.
*
* 將此 bean 的構造函數參數解析為 resolvedValues 對象。這可能涉及查找其他 bean。
* <p>此方法也用於處理靜態工廠方法的調用。
*
* 方法目的:解析配置文件中指定的方法參數
* beanName:bean名稱
* mbd:beanName對應的beanDefinition
* bw:通過它進行類型轉換
* ConstructorArgumentValues cargs:解析標簽得到的屬性,還沒有經過解析(類型轉換)
* ConstructorArgumentValues resolvedValues:已經經過解析的參數
* 返回值:返回方法需要的最小參數個數
*
*/
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
// 是否有定製的類型轉換器,沒有的話直接使用BeanWrapper進行類型轉換
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
// 構造一個BeanDefinitionValueResolver,專門用於解析constructor-arg中的value屬性,實際上還包括ref屬性,內嵌bean標簽等等
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
// minNrOfArgs 記錄執行方法要求的最小參數個數,一般情況下就是等於constructor-arg標簽指定的參數數量
int minNrOfArgs = cargs.getArgumentCount();
for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
int index = entry.getKey();
if (index < 0) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Invalid constructor argument index: " + index);
}
// 這是啥意思呢?
// 暫且你先這樣理解
// 假設A方法直接在配置文件中指定了index=3上要使用的參數,那麼這個時候A方法至少需要4個參數
// 但是其餘的3個參數可能不是通過constructor-arg標簽指定的,而是直接自動註入進來的,那麼在配置文件中我們就只配置了index=3上的參數,也就是說 int minNrOfArgs = cargs.getArgumentCount()=1,這個時候 index=3,minNrOfArgs=1, 所以 minNrOfArgs = 3+1
if (index > minNrOfArgs) {
minNrOfArgs = index + 1;
}
ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
if (valueHolder.isConverted()) {
// 如果已經轉換過了,直接添加到resolvedValues集合中
resolvedValues.addIndexedArgumentValue(index, valueHolder);
}
else {
// 解析value/ref/內嵌bean標簽等
Object resolvedValue =
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
// 將解析後的resolvedValue封裝成一個新的ValueHolder,
// 並將其source設置為解析constructor-arg得到的那個ValueHolder,
// 後期會用到這個屬性進行判斷
ConstructorArgumentValues.ValueHolder resolvedValueHolder =
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
resolvedValueHolder.setSource(valueHolder);
resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
}
}
// 對getGenericArgumentValues進行解析,代碼基本一樣,不再贅述
for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
if (valueHolder.isConverted()) {
resolvedValues.addGenericArgumentValue(valueHolder);
}
else {
Object resolvedValue =
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
resolvedValue, valueHolder.getType(), valueHolder.getName());
resolvedValueHolder.setSource(valueHolder);
resolvedValues.addGenericArgumentValue(resolvedValueHolder);
}
}
return minNrOfArgs;
}
看完怎麼確定最小方法參數個數的邏輯,接下來需要按照給定解析的構造函數參數值,創建一個參數數組以調用構造函數或工廠方法,就看一下封裝ArgumentsHolder
對象的過程,跟進代碼createArgumentArray()
方法查看。
這方法也沒啥好說,就是逐個去獲取,如果給定的參數沒有,那就從容器中獲取,這部分即為自動註入的參數。
/**
* Create an array of arguments to invoke a constructor or factory method,
* given the resolved constructor argument values.
*
* 給定解析的構造函數參數值,創建一個參數數組以調用構造函數或工廠方法。
*/
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
Class<?> paramType = paramTypes[paramIndex];
String paramName = (paramNames != null ? paramNames[paramIndex] : "");
// Try to find matching constructor argument value, either indexed or generic.
// 嘗試找到匹配的構造函數參數值,無論是索引的還是泛型的。
ConstructorArgumentValues.ValueHolder valueHolder = null;
if (resolvedValues != null) {
valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
// If we couldn't find a direct match and are not supposed to autowire,
// let's try the next generic, untyped argument value as fallback:
// it could match after type conversion (for example, String -> int).
// 如果我們找不到直接匹配並且不應該自動裝配,讓我們嘗試下一個通用的、無類型的參數值作為後備:它可以在類型轉換後匹配(例如,String -> int)。
if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
}
}
if (valueHolder != null) {
// We found a potential match - let's give it a try.
// Do not consider the same value definition multiple times!
// 我們找到了一個潛在的匹配 - 讓我們試一試。不要多次考慮相同的值定義!
usedValueHolders.add(valueHolder);
Object originalValue = valueHolder.getValue();
Object convertedValue;
if (valueHolder.isConverted()) {
convertedValue = valueHolder.getConvertedValue();
args.preparedArguments[paramIndex] = convertedValue;
}
else {
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
try {
convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" +
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
Object sourceHolder = valueHolder.getSource();
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
args.resolveNecessary = true;
args.preparedArguments[paramIndex] = sourceValue;
}
}
args.arguments[paramIndex] = convertedValue;
args.rawArguments[paramIndex] = originalValue;
}
else {
// 這部分就是超出了參數定義,需要自動註入參數的處理
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
// No explicit match found: we're either supposed to autowire or
// have to fail creating an argument array for the given constructor.
if (!autowiring) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Ambiguous argument values for parameter of type [" + paramType.getName() +
"] - did you specify the correct bean references as arguments?");
}
try {
Object autowiredArgument = resolveAutowiredArgument(
methodParam, beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
args.resolveNecessary = true;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
}
}
}
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
" to bean named '" + autowiredBeanName + "'");
}
}
return args;
}
實例化bean
工廠方法和參數都已經獲取完成了,到最後就是調用instantiate()
方法去實例化一個bean對象,跟進instantiate()
方法查看代碼。可以看到這裡就是確定一個構造策略,然後調用其instantiate()
方法。
private Object instantiate(String beanName, RootBeanDefinition mbd,
@Nullable Object factoryBean, Method factoryMethod, Object[] args) {
try {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
this.beanFactory.getAccessControlContext());
}
else {
// 確定實例化策略,調用其 instantiate() 方法,
// 該方法有兩種實現,
// 一種是普通的實現 SimpleInstantiationStrategy,
// 一種是需要使用到代理的 CglibSubclassingInstantiationStrategy 實現
return this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via factory method failed", ex);
}
}
我們看一下普通的實現SimpleInstantiationStrategy#instantiate()
,跟進代碼查看。可以看到邏輯還是比較簡單的,就是完成一些基礎設置,然後直接調用方法獲取對象返回。至此,使用工廠方法實例化bean的分析已經完成了。
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
try {
// 許可權相關,暫時忽略...
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
// 設置訪問標識
ReflectionUtils.makeAccessible(factoryMethod);
}
// 記錄上一個調用的工廠方法
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
// 設置當前調用的工廠方法
currentlyInvokedFactoryMethod.set(factoryMethod);
// 直接調用工廠方法獲取對象
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
// 返回對象
return result;
}
finally {
// 後續狀態的設置
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
catch (IllegalArgumentException ex) {
// 省略部分異常處理...
}
}
使用有參構造函數實例化bean
還是回到AbstractAutowireCapableBeanFactory#createBeanInstance()
方法里,如果經過了前面兩種處理都沒有獲得一個實例化的對象的話,那麼接下來就要使用構造函數去實例化對象了。
構造函數分為有參構造函數和無參構造函數。在這裡會先匹配有參構造函數,然後再去使用預設的無參構造函數。
這裡先討論使用有參構造函數的情況,啥時候使用有參構造函數呢?在這裡看來無非是兩種:
- 指定了一個構造函數,spring提供了一個
determineConstructorsFromBeanPostProcessors()
方法來提供一個擴展口返回一個構造函數。 - 傳入的參數不為空,這裡會進入到
autowireConstructor()
方法里進行推斷構造函數,這裡的推斷過程跟上面工廠方法推斷過程非常類似,可以參考著看看。
先來看看指定一個構造函數是怎麼處理,跟進determineConstructorsFromBeanPostProcessors()
方法,這裡可以看到就是調用了SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors()
方法,沒啥特別的。
/**
* Determine candidate constructors to use for the given bean, checking all registered
* {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
*
* 確定用於給定 bean 的候選構造函數,檢查所有已註冊的 {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}。
*
* @param beanClass the raw class of the bean
* @param beanName the name of the bean
* @return the candidate constructors, or {@code null} if none specified
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
*/
@Nullable
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}
接下來看重點autowireConstructor()
方法,這裡的實現邏輯也是委派給了ConstructorResolver
去實現。
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
* <p>This corresponds to constructor injection: In this mode, a Spring
* bean factory is able to host components that expect constructor-based
* dependency resolution.
*
* “自動裝配構造函數”(按類型使用構造函數參數)行為。
* 如果指定了顯式構造函數參數值,也適用,將所有剩餘參數與 bean 工廠中的 bean 匹配。
* <p>這對應於構造函數註入:在這種模式下,Spring bean 工廠能夠托管期望基於構造函數的依賴解析的組件。
*
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param ctors the chosen candidate constructors
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (-> use constructor argument values from bean definition)
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
跟進autowireConstructor()
方法,可以看到這裡的邏輯跟工廠方法的解析是非常類似的,根據參數去推斷合適的方法,然後獲取參數的依賴,最後利用獲取到的構造方法和參數實例化對象,詳細可見上一節,這裡不再贅述。
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
* <p>This corresponds to constructor injection: In this mode, a Spring
* bean factory is able to host components that expect constructor-based
* dependency resolution.
*
* “自動裝配構造函數”(按類型使用構造函數參數)行為。
* 如果指定了顯式構造函數參數值,也適用,將所有剩餘參數與 bean 工廠中的 bean 匹配。
* <p>這對應於構造函數註入:在這種模式下,Spring bean 工廠能夠托管期望基於構造函數的依賴解析的組件。
*
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (-> use constructor argument values from bean definition)
* 通過 getBean 方法以編程方式傳入的參數值,如果沒有則 {@code null}(-> 使用 bean 定義中的構造函數參數值)
* @return a BeanWrapper for the new instance
*/
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
// 先到緩存中嘗試獲取構造器和參數
synchronized (mbd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached constructor...
// 如果已經解析過了構造器,則直接使用緩存的構造器
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
// 如果 mbd 存在待解析的參數,直接進行解析
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
// 緩存中沒有同時存在構造器和參數,老老實實去創建獲取
if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
// 採用指定的構造函數,如果有的話。
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);
}
}
// 如果只有一個構造器,且無指定的參數並且沒有已解析的參數,直接使用該構造函數實例化對象返回
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
// 如果這個唯一的構造函數是無參構造函數,設置 mbd 屬性,實例化後返回
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
// 實例化返回
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
// Need to resolve the constructor.
// 不是無參構造函數,需要解析構造函數。
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;
// 確定最少參數的個數
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
if (constructorToUse != null && argsToUse != 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 argsHolder;
if (resolvedValues != null) {
try {
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
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;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
// 獲取構造函數和給定參數間的類型權重
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;
}
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);
}
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
使用預設構造函數實例化bean
如果前面的邏輯都沒有獲取到一個實例化bean,那麼就會走到最後的預設邏輯instantiateBean()
里,這裡會使用預設的構造函數去實例化一個bean返回。
跟進instantiateBean()
方法代碼查看。
/**
* Instantiate the given bean using its default constructor.
*
* 使用其預設構造函數實例化給定的 bean。
*
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
// 使用預設的實例化策略來實例化對象,預設為 CglibSubclassingInstantiationStrategy 實現,但是instantiate()方法只在SimpleInstantiationStrategy里有實現邏輯
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);
}
}
進入SimpleInstantiationStrategy#instantiate()
方法,這裡獲取了預設的構造函數,然後調用BeanUtils.instantiateClass(constructorToUse)
實例化對象,註意這裡沒有傳入構造參數。
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
/**
* 如果需要覆蓋或者動態替換方法,則使用cglib進行動態代理
* 因為可以在創建動態代理的同時將動態方法織入類中
* 如果沒有需要改變的方法,為了方便直接反射即可
*/
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
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);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
跟進BeanUtils.instantiateClass(constructorToUse)
的實現,這裡註意,我們上一步並沒有傳入參數,這裡使用的是無參構造函數。最後是調用了 ctor.newInstance(argsWithDefaultValues)
實例化一個對象返回。至此,所有實例化對象的方法都已經分析完成。
/**
* Convenience method to instantiate a class using the given constructor.
* <p>Note that this method tries to set the constructor accessible if given a
* non-accessible (that is, non-public) constructor, and supports Kotlin classes
* with optional parameters and default values.
*
* 使用給定構造函數實例化類的便捷方法。
* <p>請註意,如果給定一個不可訪問(即非公共)構造函數,
* 此方法會嘗試將構造函數設置為可訪問,並且支持帶有可選參數和預設值的 Kotlin 類。
*
* @param ctor the constructor to instantiate
* @param args the constructor arguments to apply (use {@code null} for an unspecified
* parameter, Kotlin optional parameters and Java primitive types are supported)
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
* @see Constructor#newInstance
*/
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
// 獲取構造器的參數
Class<?>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
// 遍歷獲取傳入的 args 參數
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
return ctor.newInstance(argsWithDefaultValues);
}
}
catch (InstantiationException ex) {
// 省略部分異常處理...
}
}
總結
這篇文章主要是分析了實例化bean的幾種方法,有哪幾種?紙面上來看是四種,但是從實際來看,後面的三種都是確定一個函數以及函數的參數來實例化一個對象,有異曲同工之妙,可以細細品味。我們實例化對象如果不指定的話,預設用的都是無參構造函數。
總的來說這篇文章比較簡單,就講了實例化對象這一件事情,相比較之前兜兜轉轉的邏輯來說可能相對層次淺一點。這篇文章的難點在於理解方法和函數之間的匹配,怎麼通過類型權重去確定一個最佳方法,這裡需要花點時間琢磨一下。
個人水平有限,如有錯誤,還請指出。
如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。