上一節(jié)我們分析了Spring通過默認(rèn)構(gòu)造函數(shù)實(shí)例化bean的過程,本小節(jié)分析Spring使用有參構(gòu)造函數(shù)實(shí)例化bean的過程。
引
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 確保此時(shí)beanClass已經(jīng)被解析
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// beanClass不為空,且beanClass的修飾符為不為public,且不允許訪問非公共構(gòu)造函數(shù)和方法,則拋出異常
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());
}
// ① Spring5.0新增的實(shí)例化策略,如果設(shè)置了該策略,將會覆蓋構(gòu)造方法和工廠方法實(shí)例化策略
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// ② 如果有工廠方法的話,則使用工廠方法實(shí)例化bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// ③ 當(dāng)創(chuàng)建一個(gè)相同的bean時(shí),使用之間保存的快照
// 這里可能會有一個(gè)疑問,什么時(shí)候會創(chuàng)建相同的bean呢?
// ③-->① 單例模式: Spring不會緩存該模式的實(shí)例,那么對于單例模式的bean,什么時(shí)候會用到該實(shí)例化策略呢?
// 我們知道對于IoC容器除了可以索取bean之外,還能銷毀bean,當(dāng)我們調(diào)用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),
// 銷毀bean時(shí),容器是不會銷毀已經(jīng)解析的構(gòu)造函數(shù)快照的,如果再次調(diào)用xmlBeanFactory.getBean(myBeanName)時(shí),就會使用該策略了.
// ③-->② 原型模式: 對于該模式的理解就簡單了,IoC容器不會緩存原型模式bean的實(shí)例,當(dāng)我們第二次向容器索取同一個(gè)bean時(shí),就會使用該策略了.
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果該bean已經(jīng)被解析過
if (resolved) {
// 使用已經(jīng)解析過的構(gòu)造函數(shù)實(shí)例化
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
// 使用默認(rèn)無參構(gòu)造函數(shù)實(shí)例化
else {
return instantiateBean(beanName, mbd);
}
}
// ④ 確定需要使用的構(gòu)造函數(shù)
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// ⑤ 無任何的特殊處理,則使用默認(rèn)的無參構(gòu)造函數(shù)實(shí)例化bean
return instantiateBean(beanName, mbd);
}
從代碼中可以看到,如果該bean的構(gòu)造函數(shù)已經(jīng)被解析并緩存的話,則優(yōu)先使用已經(jīng)被解析的構(gòu)造函數(shù)實(shí)例化,否則解析bean的構(gòu)造函數(shù)并實(shí)例化。
在這里我們再一次看到了Spring對緩存的應(yīng)用,而且解析構(gòu)造函數(shù)是一個(gè)比較復(fù)雜耗時(shí)的操作,所以這里需要緩存已經(jīng)解析的構(gòu)造函數(shù)。
1.測試用例
打開day01下的MyTest類:
@Test
public void test2() {
// 指定構(gòu)造器
System.out.println("有參構(gòu)造器");
Dog dog2 = xmlBeanFactory.getBean("dog2", Dog.class);
dog2.sayHello();
}
2.解析構(gòu)造函數(shù)
- 創(chuàng)建ConstructorResolver對象
protected BeanWrapper autowireConstructor(
String beanName,
RootBeanDefinition mbd,
@Nullable Constructor<?>[] ctors,
@Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
- 解析構(gòu)造函數(shù)
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 1、 判斷有無顯式指定參數(shù),如果有則優(yōu)先使用,如xmlBeanFactory.getBean("cat", "美美",3);
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
// 2、 沒有顯式指定參數(shù),則解析配置文件中的參數(shù)
else {
Object[] argsToResolve = null;
// 3、 優(yōu)先嘗試從緩存中獲取,spring對參數(shù)的解析過程是比較復(fù)雜也耗時(shí)的,所以這里先嘗試從緩存中獲取已經(jīng)解析過的構(gòu)造函數(shù)參數(shù)
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;
}
}
}
// 緩存中存在,則解析構(gòu)造函數(shù)參數(shù)類型
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
// 4、 緩存中不存在,則需要解析構(gòu)造函數(shù)參數(shù),以確定使用哪一個(gè)構(gòu)造函數(shù)來進(jìn)行實(shí)例化
if (constructorToUse == null) {
boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
// 這里定義了一個(gè)變量,來記錄最小的構(gòu)造函數(shù)參數(shù)個(gè)數(shù),其作用可以參見下面解釋...
int minNrOfArgs;
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// Take specified constructors, if any.
// 5、 使用指定的構(gòu)造函數(shù)(如果有的話)。
// 注意: 5.1、 這里說的指定構(gòu)造函數(shù),并不是我們在配置文件中指定的構(gòu)造函數(shù),而是通過解析SmartInstantiationAwareBeanPostProcessor得出的構(gòu)造函數(shù)
// 參見AbstractAutowireCapableBeanFactory-->determineConstructorsFromBeanPostProcessors(beanClass, beanName),
// 就是在本方法被調(diào)用之前執(zhí)行的解析操作
// 5.2、 即便解析出來的構(gòu)造函數(shù)不為空,但是大家要注意,candidates是個(gè)數(shù)組,下一步依然還是要對candidates進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
// 6、 如果指定的構(gòu)造函數(shù)不存在,則根據(jù)方法訪問級別,獲取該bean所有的構(gòu)造函數(shù)
// 對于本例來分析,應(yīng)該會獲取到四個(gè)構(gòu)造函數(shù)Cat(),Cat(String name),Cat(int age),Cat(String name, int age)
// 注意:該處獲取到的構(gòu)造函數(shù),并不是配置文件中定義的構(gòu)造函數(shù),而是bean類中的構(gòu)造函數(shù)
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);
}
}
// 7、 對構(gòu)造函數(shù)按照參數(shù)個(gè)數(shù)和參數(shù)類型進(jìn)行排序,參數(shù)最多的構(gòu)造函數(shù)會排在第一位
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
// 8、 循環(huán)所有bean類中的構(gòu)造函數(shù),解析確定使用哪一個(gè)構(gòu)造函數(shù)
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
// 如果constructorToUse不為空,且參數(shù)個(gè)數(shù)大于當(dāng)前循環(huán)的構(gòu)造函數(shù)參數(shù)個(gè)數(shù),則直接終止循環(huán),因?yàn)榻馕龅腷ean類構(gòu)造函數(shù)已經(jīng)經(jīng)過排序
// 問題: 進(jìn)入該if語句的條件是constructorToUse==null? 該處判斷是否多余.. ?
// 帶著這個(gè)問題,可以接著看源碼...該處的設(shè)計(jì)還是值得參考的
if (constructorToUse != null && argsToUse.length > paramTypes.length) {
break;
}
// 如果從bean類中解析到的構(gòu)造函數(shù)個(gè)數(shù)小于從beanDefinition中解析到的構(gòu)造函數(shù)個(gè)數(shù),
// 那么肯定不會使用該方法實(shí)例化,循環(huán)繼續(xù)
// 簡單的理解:beanDefinition中的構(gòu)造函數(shù)和bean類中的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)不相等,那么肯定不會使用該構(gòu)造函數(shù)實(shí)例化
if (paramTypes.length < minNrOfArgs) {
continue;
}
// 9、 獲取ArgumentsHolder對象,直接理解為一個(gè)參數(shù)持有者即可
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);
}
// 10、 通過構(gòu)造函數(shù)參數(shù)權(quán)重對比,得出最適合使用的構(gòu)造函數(shù)
// 先判斷是返回是在寬松模式下解析構(gòu)造函數(shù)還是在嚴(yán)格模式下解析構(gòu)造函數(shù)。(默認(rèn)是寬松模式)
// 10.1、對于寬松模式:例如構(gòu)造函數(shù)為(String name,int age),配置文件中定義(value="美美",value="3")
// 那么對于age來講,配置文件中的"3",可以被解析為int也可以被解析為String,
// 這個(gè)時(shí)候就需要來判斷參數(shù)的權(quán)重,使用ConstructorResolver的靜態(tài)內(nèi)部類ArgumentsHolder分別對字符型和數(shù)字型的參數(shù)做權(quán)重判斷
// 權(quán)重越小,則說明構(gòu)造函數(shù)越匹配
// 10.2、對于嚴(yán)格模式:嚴(yán)格返回權(quán)重值,不會根據(jù)分別比較而返回比對值
// 10.3、minTypeDiffWeight = Integer.MAX_VALUE;而權(quán)重比較返回結(jié)果都是在Integer.MAX_VALUE做減法,起返回最大值為Integer.MAX_VALUE
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
// 如果此構(gòu)造函數(shù)表示最接近的匹配,則選擇此構(gòu)造函數(shù)
if (typeDiffWeight < minTypeDiffWeight) {
// 將解析到的構(gòu)造函數(shù)賦予constructorToUse,這也是我們上面問題疑問的答案所在處
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);
}
}
// 11、 異常處理
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);
}
// 12、 緩存解析的構(gòu)造函數(shù)
if (explicitArgs == null) {
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
try {
// 13、 獲取實(shí)例化策略并執(zhí)行實(shí)例化
final InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
Object beanInstance;
if (System.getSecurityManager() != null) {
final Constructor<?> ctorToUse = constructorToUse;
final Object[] argumentsToUse = argsToUse;
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
strategy.instantiate(mbd, beanName, this.beanFactory, ctorToUse, argumentsToUse),
this.beanFactory.getAccessControlContext());
}
else {
beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
}
// 14、 返回BeanWrapper包裝類
bw.setBeanInstance(beanInstance);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via constructor failed", ex);
}
}
這個(gè)過程是相當(dāng)復(fù)雜的,逐步分析其過程:
2.1 判斷有無顯式指定參數(shù),如果有則優(yōu)先使用,如xmlBeanFactory.getBean("cat", "美美",3);
2.2 優(yōu)先嘗試從緩存中獲取,spring對參數(shù)的解析過程是比較復(fù)雜也耗時(shí)的,所以這里先嘗試從緩存中獲取已經(jīng)解析過的構(gòu)造函數(shù)參數(shù)
2.3 緩存中不存在,則需要解析構(gòu)造函數(shù)參數(shù),以確定使用哪一個(gè)構(gòu)造函數(shù)來進(jìn)行實(shí)例化
一個(gè)類可能存在多個(gè)構(gòu)造函數(shù),如Dog(String name,int age);Dog(String name);Dog(int age)等等,所以需要解析對構(gòu)造函數(shù)進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)。
2.4 使用指定的構(gòu)造函數(shù)(如果有的話)。
這里說的指定構(gòu)造函數(shù),并不是我們在配置文件中指定的構(gòu)造函數(shù),而是通過解析SmartInstantiationAwareBeanPostProcessor得出的構(gòu)造函數(shù)。
參見AbstractAutowireCapableBeanFactory-->determineConstructorsFromBeanPostProcessors(beanClass, beanName),就是在本方法被調(diào)用之前執(zhí)行的解析操作,即便解析出來的構(gòu)造函數(shù)不為空,但是大家要注意,candidates是個(gè)數(shù)組,下一步依然還是要對candidates進(jìn)行解析,以確定使用哪一個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化。
2.5 如果指定的構(gòu)造函數(shù)不存在,則根據(jù)方法訪問級別,獲取該bean所有的構(gòu)造函數(shù)
需要注意,該步驟獲取的是類的構(gòu)造函數(shù),而不是在配置文件中的構(gòu)造函數(shù)。
2.6 對構(gòu)造函數(shù)按照參數(shù)個(gè)數(shù)和參數(shù)類型進(jìn)行排序,參數(shù)最多的構(gòu)造函數(shù)會排在第一位
2.7 循環(huán)所有bean類中的構(gòu)造函數(shù),解析確定使用哪一個(gè)構(gòu)造函數(shù)
首先因?yàn)闃?gòu)造函數(shù)已經(jīng)按照參數(shù)的個(gè)數(shù)排序,參數(shù)個(gè)數(shù)最多的排在最前面,所以判斷如若解析出來的構(gòu)造函數(shù)個(gè)數(shù)小于BeanDefinition中的構(gòu)造函數(shù)個(gè)數(shù),那么肯定不會使用該構(gòu)造函數(shù)進(jìn)行實(shí)例化,那么循環(huán)會繼續(xù)。
其次將解析到的構(gòu)造函數(shù)封裝至ArgumentsHolder對象。
最后通過構(gòu)造函數(shù)參數(shù)權(quán)重對比,得出最適合使用的構(gòu)造函數(shù)。
2.8 處理異常,緩存解析過的構(gòu)造函數(shù)。
2.9 獲取實(shí)例化策略并執(zhí)行實(shí)例化
同樣這里也會有反射或CGLIB實(shí)例化bean,具體的細(xì)節(jié),上一節(jié)已經(jīng)分析過。
2.10 返回BeanWrapper包裝類
3.總結(jié)
本節(jié)的重點(diǎn)和難點(diǎn)在于對構(gòu)造函數(shù)的確定,大家可以多定義一些有參構(gòu)造函數(shù),多通過調(diào)試來加深理解。