highlight: vs2015
theme: channing-cyan
日积月累,水滴石穿 😄
前言
各位小伙伴好,这是Spring源码
系列的第八篇文章。本篇篇幅较长(五千八百字),建议收藏慢慢品尝。
上篇已经分析了doCreateBean
方法中的关键点之一:createBeanInstance
。
该方法的作用就是实例化 Bean。前文提到在 createBeanInstance
中实例化 Bean一共有四种方式:
- Supplier 回调:
obtainFromSupplier()
- 工厂方法初始化:
instantiateUsingFactoryMethod()
- 构造函数自动注入初始化:
autowireConstructor()
- 默认构造函数注入:
instantiateBean()
在上篇博客 Spring源码(七)-Supplier、工厂方法实例化Bean-createBeanInstance 中分析了 Supplier 回调和工厂方法,这篇就来分析其他两种方式。还是在AbstractAutowireCapableBeanFactory
.createBeanInstance
方法中。
// 上述代码省略。。。
boolean resolved = false;
boolean autowireNecessary = false;
//调用getBean()时不传入参数
if(args == null) {
synchronized(mbd.constructorArgumentLock)
// 当作用域为原型时、多次调用getBean()时不传入参数,
//从缓存中获取这段逻辑才会被执行
// resolvedConstructorOrFactoryMethod 缓存了已解析的构造函数或工厂方法
if(mbd.resolvedConstructorOrFactoryMethod != null) {
// resolved为true,表示当前bean的构造方法已经确定了,
//也代表该Bean之前被解析过
resolved = true;
// constructorArgumentsResolved:将构造函数参数标记为已解析
//true就是标记为了已解析
// 默认为 false。
// 如果autowireNecessary为true说明是采用有参构造函数注入
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
//resolved = true,表示当前bean的构造方法已经确定了
if(resolved) {
// autowireNecessary = true ,表示采用有参构造函数注入
if(autowireNecessary) {
// 采用有参构造函数注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 构造方法已经确定了,但是没有确定构造方法参数
//那就表示没有构造方法参数,用无参的构造方法来实例化bean
return instantiateBean(beanName, mbd);
}
}
上面这段代码的逻辑为:如果没有显示的传入参数,判断 resolvedConstructorOrFactoryMethod
是否不为空,如果不为空,则存在缓存,直接使用已经解析了的构造函数。然后根据 autowireNecessary
参数来判断是使用有参构造函数自动注入还是使用无参构造函数注入。
autowireConstructor
先来看一下 autowireConstructor
方法,也就是有参构造函数自动注入。进入 ConstructorResolver
类的 autowireConstructor
方法。
该方法有四个参数:
- beanName:当前 Bean 的名称。
- mbd:当前 Bean 的定义。
- chosenCtors:经过筛选的构造方法列表。可能为 null。
- explicitArgs:开发者在调用 getBean 方法时显示传递的参数。
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;
// getBean()方法指定了构造方法参数
if(explicitArgs != null) {
argsToUse = explicitArgs;
} else {
// 从缓存中获取构造方法和构造方法参数
// 当作用域为原型时并多次调用 getBean()时没有传递参数
// 创建 Bean 是会走这段缓存逻辑。
// 为单例只会从一次,之后的 getBean() 都会从单例池获取。
Object[] argsToResolve = null;
synchronized(mbd.constructorArgumentLock) {
// resolvedConstructorOrFactoryMethod:缓存已解析的构造函数或工厂方法
constructorToUse = (Constructor <? > ) mbd.resolvedConstructorOrFactoryMethod;
// 找到了mbd中缓存的构造方法
// constructorArgumentsResolved:将构造函数参数标记为已解析
//true就是标记为了已解析
if(constructorToUse != null && mbd.constructorArgumentsResolved) {
// resolvedConstructorArguments:获得已完全解析的构造函数参数
//(参数类型已经确定,能够直接进行使用)
// 正常情况下 resolvedConstructorArguments 的值就是 null
argsToUse = mbd.resolvedConstructorArguments;
if(argsToUse == null) {
//获得部分准备好的构造函数参数(该参数的类型是不确定的,需要进行解析)
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
//如果存在构造函数参数,那么则对参数值进行类型转化
//如给定方法的构造函数 Person(int) 则通过此方法后就会把配置中的
// "5“ 转换为 5
//<constructor-arg index="0" value="5"/>
//缓存中的值可能是原始值也可能是最终值
if(argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw,
constructorToUse, argsToResolve, true);
}
}
// 如果待使用的构造方法为null,或待使用的构造方法参数为null,也就是没有缓存
// 这个if代码很长,但其实就去找构造方法、构造方法参数
// 并赋值给 constructorToUse、argsToUse
if(constructorToUse == null || argsToUse == null) {
// chosenCtors表示所指定的构造方法
// 没有指定则获取beanClass中的所有的构造方法作为候选者
// 从这些构造方法中选择一个构造方法
Constructor <? > [] candidates = chosenCtors;
if(candidates == null) {
Class <? > beanClass = mbd.getBeanClass();
try {
// mbd.isNonPublicAccessAllowed():默认为true
// getDeclaredConstructors():获得本类所有构造方法
// getConstructors:获得本类的所有公有构造方法
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);
}
}
// 如果只有一个构造方法,并且没有显示指定构造方法参数
// 并且在xml中没有使用constructor-arg标签
// 则需要判断是不是无参构造方法,
// 如果是 则使用无参构造方法进行实例化
if(candidates.length == 1 && explicitArgs == null &&
!mbd.hasConstructorArgumentValues()) {
Constructor <? > uniqueCandidate = candidates[0];
// 判断是不是无参构造方法
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;
}
}
// 如果入参 chosenCtors不为null,也就是找到了构造方法,或者autowireMode是构造方法自动注入
//则可能要自动选择构造方法
boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() ==
AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
// 记录解析后的构造方法参数值
ConstructorArgumentValues resolvedValues = null;
// minNrOfArgs:表示所有构造方法中,参数个数最少的构造方法的参数个数是多少
int minNrOfArgs;
if(explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
} else {
// 从BeanDefinition中获取所设置的构造方法参数值
// 值来源于 constructor-arg标签中的 index属性的值 这个值可以随便写
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
// 记录解析后的构造方法参数值
resolvedValues = new ConstructorArgumentValues();
// 解析参数个数 值来源于 constructor-arg标签中的 index属性的值 这个值可以随便写
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw,
cargs, resolvedValues);
}
// 按构造方法的参数个数降序排序,先排序public构造函数,参数降序排列
// 然后排序非public 的构造函数,参数降序排列
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set < Constructor <? >> ambiguousConstructors = null;
LinkedList < UnsatisfiedDependencyException > causes = null;
// 遍历构造方法,找到一个最合适的
// 先看参数列表最长的构造方法,根据每个参数的参数类型和参数名去找bean
for(Constructor <? > candidate: candidates) {
// 当前构造方法的参数个数
int parameterCount = candidate.getParameterCount();
// 已经找到选用的构造函数 且该参数个数大于当前遍历的,则不用继续遍历了
// 上面已经按照参数个数降序排列了
if(constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
break;
}
// 在遍历某个构造方法时,如果当前遍历的参数个数小于所指定的参数个数
// 则忽略该构造方法
// minNrOfArgs已经是最小的了,比他还小
// 肯定是不符合我所需要的,就不必往下执行了
if(parameterCount < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
// 当前遍历到的构造方法的参数类型
Class <? > [] paramTypes = candidate.getParameterTypes();
// 当 getBean()方法没有显示指定构造方法参数,resolvedValues 不为 null
if(resolvedValues != null) {
try {
// 获取参数名
// 查看是否在构造方法上使用@ConstructorProperties
// 注解来定义构造方法参数的名字
String[] paramNames = ConstructorPropertiesChecker.evaluate(
candidate, parameterCount);
if(paramNames == null) {
// ParameterNameDiscoverer用于解析方法、构造函数上的参数名称
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if(pnd != null) {
//获取构造函数的参数名称
paramNames = pnd.getParameterNames(candidate);
}
}
// 根据当前构造方法的参数类型和参数名从beanFactory中得到bean作为参数值
// resolvedValues:解析后的构造方法参数值
// paramTypes:当前构造方法中每个参数的属性类型
// paramNames:当前构造方法中每个参数的属性名称
// getUserDeclaredConstructor(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 {
// getBean()方法指定了构造方法参数值
// 当前构造方法参数个数与传入的参数个数不相等,跳出本次循环
if(parameterCount != explicitArgs.length) {
continue;
}
// 如果参数个数匹配,则把所有参数值封装为一个ArgumentsHolder对象
argsHolder = new ArgumentsHolder(explicitArgs);
}
// 执行到这里,表示当前构造方法可用,并且也找到了对应的构造方法参数值
// 但是还需要判断,当前构造方法是不是最合适的,也许还有另外的构造方法更合适
// 根据参数类型和参数值计算权重
// Lenient宽松,默认宽松模式是开启的
// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常
// 宽松模式:使用具有"最接近的模式"进行匹配
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) {
// 如果权重一样,则记录在ambiguousConstructors中,继续遍历下一个构造方法
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)"
);
}
// 如果存在权重一样的构造方法并且不是宽松模式,也报错
// 因为权重一样,Spring不知道该用哪个
// 如果是宽松模式则不会报错,Spring会用找到的第一个
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);
}
// 如果不是通过getBean()方法指定的参数,那么就把找到的构造方法参数进行缓存
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;
}
代码很长,但总体来说就是确定一件事情。确定构造函数、构造函数参数,然后调用 instantiate
方法进行 bean 的实例化。
总体思路:
-
- 先检查是否指定了具体的构造方法和构造方法参数值,或者在BeanDefinition中缓存了具体的构造方法或构造方法参数值,如果存在那么则直接使用该构造方法进行实例化。
-
- 如果没有确定的构造方法或构造方法参数值,又分为以下流程:
- a. 如果没有确定构造方法,那么则找出类中所有的构造方法。
- b. 如果只有一个无参的构造方法,那么直接使用无参的构造方法进行实例化。
- c. 如果有多个构造方法或者当前Bean的注入方式是构造方法自动注入,则要自动选择构造方法。
- d. 根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数。如果没有指定,从
BeanDefinition
的constructorArgumentValues
属性获取。 - e. 对所有的构造方法进行排序,public 构造函数优先参数数量降序,非 public构造函数参数数量降序。
- f. 遍历每个构造方法。
- g. 如果调用
getBean
方法时,没有显示指定构造方法参数值,那么则根据当前循环到的构造方法得到构造参数类型、构造参数名称与解析后的构造方法参数值(resolvedValues
)进行匹配,构建ArgumentsHolder
对象。 - h. 如果调用 getBean 方法时,指定构造方法参数值,就直接利用传入的构造方法参数值构建
ArgumentsHolder
对象。 - i. 如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值,这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的(分越少优先级越高)。
为什么分越少优先级越高?
主要是计算找到的bean和构造方法参数类型匹配程度有多高
假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D
- 如果构造方法的参数类型为A,那么完全匹配,得分为0
- 如果构造方法的参数类型为B,那么得分为2
- 如果构造方法的参数类型为C,那么得分为4
- 如果构造方法的参数类型为D,那么得分为1
可以直接使用如下代码进行测试:
Object[] objects = new Object[]{new A()};
// 0
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects));
// 2
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects));
// 4
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects));
// 1
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects));
以上知识学习自图灵学院
。
举例
各位小伙伴可以下去跑一下例子,打打断点。
举例一、
- 1、创建 A、B 类,其中 A 类有 B 属性、name 属性。并且在
A(B b)
这个构造方法加上@Autowired
注解,表示要使用这个构造方法实例化对象。
public class A {
private B b;
private String name;
public A() {
}
@Autowired
public A(B b) {
this.b = b;
}
public A(String name) {
this.name = name;
}
public A(B b, String name) {
this.b = b;
this.name = name;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "A{" +
"b=" + b +
", name='" + name + '\'' +
'}';
}
}
public class B {
}
- 2、spring-config4.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.gongj.create.A" id="aa"></bean>
<bean class="com.gongj.create.B" id="bb"></bean>
<context:component-scan base-package="com.gongj.create">
</context:component-scan>
</beans>
- 3、启动
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"spring-config4.xml");
A a = (A) context.getBean("aa");
System.out.println(a);
}
结果:A{b=com.gongj.create.B@27808f31, name='null'}
举例二、
- 1、修改 spring-config4.xml 配置内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--这里使用 constructor-arg-->
<bean class="com.gongj.create.A" id="aa">
<constructor-arg index="1" value="gongj"></constructor-arg>
<constructor-arg index="0" ref="bb"></constructor-arg>
</bean>
<bean class="com.gongj.create.B" id="bb"></bean>
<context:component-scan base-package="com.gongj.create">
</context:component-scan>
</beans>
- 2、启动
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config4.xml");
A a = (A)context.getBean("aa");
System.out.println(a);
}
各位可以猜猜运行结果是什么?结果是报错!
那小杰将@Autowired
注解标注在A(B b, String name)
构造方法上,结果又是如何?
@Autowired
public A(B b, String name) {
this.b = b;
this.name = name;
}
结果:A{b=com.gongj.create.B@3e57cd70, name='gongj'}
举例三、
- 1、将 A 的作用域修改为
prototype
。
<bean class="com.gongj.create.A" id="aa" scope="prototype">
<constructor-arg index="0" ref="bb"></constructor-arg>
<constructor-arg index="1" value="gongj"></constructor-arg>
</bean>
- 2、启动
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config4.xml");
A a = (A)context.getBean("aa");
System.out.println(a+ "-===" + a.hashCode());
A a2 = (A)context.getBean("aa",new B(),"uanjfjef");
System.out.println(a2+ "-===" + a2.hashCode());
A a3 = (A)context.getBean("aa");
System.out.println(a3 + "-===" + a3.hashCode());
}
结果:
A{b=com.gongj.create.B@3e57cd70, name='gongj'}-===161960012
//用了传入的值
A{b=com.gongj.create.B@2c039ac6, name='uanjfjef'}-===1484594489
// 用了缓存的值,而且并没有缓存传入的值
A{b=com.gongj.create.B@3e57cd70, name='gongj'}-===1489069835
其中有几个方法简单的提一下:
resolveConstructorArguments
解析构造方法的参数值。可能会创建Bean。比如上述举例二。
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd,
BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
// 获得当前 beanFatory 类型转换器
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
// 获得当前 beanFatory 类型转换器为 null,则使用 bw,bw 实现了 TypeConverter
TypeConverter converter = (customConverter != null ? customConverter : bw);
//为给定的BeanFactory和BeanDefinition创建一个BeanDefinitionValueResolver
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(
this.beanFactory, beanName, mbd, converter);
// 获得 constructor-arg 标签的个数
int minNrOfArgs = cargs.getArgumentCount();
// 先遍历 cargs 中的indexedArgumentValues
// indexedArgumentValues存的是某个index对应的构造方法参数值
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);
}
if(index > minNrOfArgs) {
minNrOfArgs = index + 1;
}
// 获得构造方法参数值
ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
if(valueHolder.isConverted()) {
// 把该数据添加到 resolvedValues 对象中
resolvedValues.addIndexedArgumentValue(index, valueHolder);
} else {
// 把“值”转化为对应的类型
// 获得 constructor-arg 的 value
// resolveValueIfNecessary:这里可能会创建bean 因为 constructor-arg 里面有个 ref 属性
//可以引用其他bean。
Object resolvedValue = valueResolver.resolveValueIfNecessary(
"constructor argument", valueHolder.getValue());
ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues
.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
resolvedValueHolder.setSource(valueHolder);
// 把该数据添加到 resolvedValues 对象中
resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
}
}
// for循环结束=====
// 把genericArgumentValues中的值进行类型转化然后添加到resolvedValues中去
// 根据上述循环逻辑差不多
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);
}
}
// for循环结束-------
return minNrOfArgs;
}
首先获得当前 Bean 的 constructor-arg
标签的个数 minNrOfArgs
,然后遍历 cargs 中的 indexedArgumentValues
元素,获取每个对象的key
(也就是constructor-arg
标签的index
元素),与 minNrOfArgs
进行比较。顺便将 key
和 value
添加到 resolvedValues
对象中。
createArgumentArray
构造参数持有者。拥有最终要使用的构造参数。
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
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;
// 如果指定了构造方法参数值,那么则看当前paramType有没有对应的值
if(resolvedValues != null) {
// 拿到第paramIndex位置的构造方法参数值,会根据传进去的类型、名称进行匹配
//如果类型、名称不相等则返回null
valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType,
paramName, usedValueHolders);
// 如果找不到直接匹配项,尝试一个通用的,无类型的参数值作为后备,
// 类型转换后可以匹配(例如,String-> int)。
if(valueHolder == null && (!autowiring || paramTypes.length ==
resolvedValues.getArgumentCount())) {
valueHolder = resolvedValues.getGenericArgumentValue(null, null,
usedValueHolders);
}
}
// 如果找到了对应的值,则进行类型转化,把转化前的值存在args.rawArguments中
// 转化后的值存在args.arguments中
if(valueHolder != null) {
usedValueHolders.add(valueHolder);
Object originalValue = valueHolder.getValue();
Object convertedValue;
if(valueHolder.isConverted()) {
convertedValue = valueHolder.getConvertedValue();
args.preparedArguments[paramIndex] = convertedValue;
} else {
// 为给定的方法或构造函数创建一个新的MethodParameter。
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();
// 将 resolveNecessary 变量赋值为 true,该变量会影响
// resolvedConstructorArguments:缓存完全解析的构造函数参数
// preparedConstructorArguments:缓存部分准备好的构造函数参数
// 这两个参数是谁为 null 值
args.resolveNecessary = true;
args.preparedArguments[paramIndex] = sourceValue;
}
}
args.arguments[paramIndex] = convertedValue;
args.rawArguments[paramIndex] = originalValue;
} else {
// 如果上述逻辑,没有找到对应的值。举例中的举例一 就会走该代码分支
// 为给定的方法或构造函数创建一个新的MethodParameter。
MethodParameter methodParam = MethodParameter.forExecutable(executable,
paramIndex);
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?");
}
// 如果 autowiring 的值为 true,也就是
// 如果入参 chosenCtors不为null,也就是找到了构造方法
//或者autowireMode是构造方法自动注入,则可能要自动选择构造方法
//boolean autowiring = (chosenCtors != null ||
//mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
try {
// 该方法内部会调用 resolveDependency 方法,得到一个bean,resolveDependency 方法很重要。
Object autowiredArgument = resolveAutowiredArgument(methodParam,
beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
// 将 resolveNecessary 变量赋值为 true,该变量会影响
// resolvedConstructorArguments:缓存完全解析的构造函数参数
// preparedConstructorArguments:缓存部分准备好的构造函数参数
// 这两个参数是否为 null
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;
}
根据当前构造方法的参数类型、参数名称与上面解析得到的构造方法参数值进行匹配,如果匹配成功返回构造方法的参数值,进行类型转换。然后将必要的属性进行赋值,最后进行返回。
instantiate
instantiate
方法,该方法在 SimpleInstantiationStrategy
类中实现。
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName,
BeanFactory owner,
final Constructor <? > ctor, Object...args) {
//bd.hasMethodOverrides():是否为此bean定义了方法重写,方法返回true说明重写了
if(!bd.hasMethodOverrides()) {
// 没有重写,直接使用反射实例化即可
if(System.getSecurityManager() != null) {
// use own privileged to change accessibility (when security is on)
AccessController.doPrivileged((PrivilegedAction < Object > )() - > {
ReflectionUtils.makeAccessible(ctor);
return null;
});
}
// 通过BeanUtils直接使用构造器对象实例化bean
return BeanUtils.instantiateClass(ctor, args);
}
else {
// 重写了
// 生成CGLIB创建的子类对象
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
}
}
如果该 Bean 没有配置 lookup-method
、replaced-method
标签或者 @Lookup
注解,则直接通过反射的方式实例化 Bean 。如果配置了则需要使用 CGLIB
进行动态代理。
instantiateBean
使用无参的构造方法来实例化bean。
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 {
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);
}
}
instantiateBean
方法相比于 instantiateUsingFactoryMethod
、 autowireConstructor
方法实在是太简单了,因为它没有参数,所以不需要确定构造方法、构造方法参数,直接调用实例化策略进行实例化就可以了。
下篇介绍 doCreateBean()
方法中的最后一个方法:determineConstructorsFromBeanPostProcessors
,确定构造函数。
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。