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的幾種方法,有哪幾種?紙面上來看是四種,但是從實際來看,後面的三種都是確定一個函式以及函式的引數來例項化一個物件,有異曲同工之妙,可以細細品味。我們例項化物件如果不指定的話,預設用的都是無參建構函式。
總的來說這篇文章比較簡單,就講了例項化物件這一件事情,相比較之前兜兜轉轉的邏輯來說可能相對層次淺一點。這篇文章的難點在於理解方法和函式之間的匹配,怎麼通過型別權重去確定一個最佳方法,這裡需要花點時間琢磨一下。
個人水平有限,如有錯誤,還請指出。
如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。