摘要:你也会了解到构造对象的两种策略。构造方法参数数量低于配置的参数数量,则忽略当前构造方法,并重试。通过默认构造方法创建对象看完了上面冗长的逻辑,本节来看点轻松的吧通过默认构造方法创建对象。
1. 简介
本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程。本篇文章,我们就从战术的层面上,详细分析doCreateBean方法中的一个重要的调用,即createBeanInstance方法。在本篇文章中,你将看到三种不同的构造 bean 对象的方式。你也会了解到构造 bean 对象的两种策略。如果你对这些内容感兴趣,那么不妨继续往下读。我会在代码进行大量的注解,相信能帮助你理解代码逻辑。好了,其他的就不多说了,进入正题吧。
2. 源码分析 2.1 创建 bean 对象的过程本节,我们一起来来分析一下本篇文章的主角createBeanInstance方法。按照惯例,我们还是先分析一下方法的大致脉络,然后我们再按照这个脉络去分析一些重要的调用。So. Let`s go → ↓
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
Class beanClass = resolveBeanClass(mbd, beanName);
/*
* 检测类的访问权限。默认情况下,对于非 public 的类,是允许访问的。
* 若禁止访问,这里会抛出异常
*/
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());
}
/*
* 如果工厂方法不为空,则通过工厂方法构建 bean 对象。这种构建 bean 的方式
* 就不深入分析了,有兴趣的朋友可以自己去看一下。
*/
if (mbd.getFactoryMethodName() != null) {
// 通过“工厂方法”的方式构建 bean 对象
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
/*
* 当多次构建同一个 bean 时,可以使用此处的快捷路径,即无需再次推断应该使用哪种方式构造实例,
* 以提高效率。比如在多次构建同一个 prototype 类型的 bean 时,就可以走此处的捷径。
* 这里的 resolved 和 mbd.constructorArgumentsResolved 将会在 bean 第一次实例
* 化的过程中被设置,在后面的源码中会分析到,先继续往下看。
*/
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
// 通过“构造方法自动注入”的方式构造 bean 对象
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 通过“默认构造方法”的方式构造 bean 对象
return instantiateBean(beanName, mbd);
}
}
// 由后置处理器决定返回哪些构造方法,这里不深入分析了
Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
/*
* 下面的条件分支条件用于判断使用什么方式构造 bean 实例,有两种方式可选 - 构造方法自动
* 注入和默认构造方法。判断的条件由4部分综合而成,如下:
*
* 条件1:ctors != null -> 后置处理器返回构造方法数组是否为空
*
* 条件2:mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR
* -> bean 配置中的 autowire 属性是否为 constructor
* 条件3:mbd.hasConstructorArgumentValues()
* -> constructorArgumentValues 是否存在元素,即 bean 配置文件中
* 是否配置了
* 条件4:!ObjectUtils.isEmpty(args)
* -> args 数组是否存在元素,args 是由用户调用
* getBean(String name, Object... args) 传入的
*
* 上面4个条件,只要有一个为 true,就会通过构造方法自动注入的方式构造 bean 实例
*/
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 通过“构造方法自动注入”的方式构造 bean 对象
return autowireConstructor(beanName, mbd, ctors, args);
}
// 通过“默认构造方法”的方式构造 bean 对象
return instantiateBean(beanName, mbd);
}
以上就是 createBeanInstance 方法的源码,不是很长。配合着注释,应该不是很难懂。下面我们来总结一下这个方法的执行流程,如下:
检测类的访问权限,若禁止访问,则抛出异常
若工厂方法不为空,则通过工厂方法构建 bean 对象,并返回结果
若构造方式已解析过,则走快捷路径构建 bean 对象,并返回结果
如第三步不满足,则通过组合条件决定使用哪种方式构建 bean 对象
这里有三种构造 bean 对象的方式,如下:
通过“工厂方法”的方式构造 bean 对象
通过“构造方法自动注入”的方式构造 bean 对象
通过“默认构造方法”的方式构造 bean 对象
下面我将会分析第2和第3种构造 bean 对象方式的实现源码。至于第1种方式,实现逻辑和第2种方式较为相似。所以就不分析了,大家有兴趣可以自己看一下。
2.2 通过构造方法自动注入的方式创建 bean 实例本节,我将会分析构造方法自动注入的实现逻辑。代码逻辑较为复杂,需要大家耐心阅读。代码如下:
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, Constructor[] ctors, Object[] explicitArgs) {
// 创建 ConstructorResolver 对象,并调用其 autowireConstructor 方法
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
Constructor[] chosenCtors, final Object[] explicitArgs) {
// 创建 BeanWrapperImpl 对象
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 确定参数值列表(argsToUse)
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
// 获取已解析的构造方法
constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// 获取已解析的构造方法参数列表
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
// 若 argsToUse 为空,则获取未解析的构造方法参数列表
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
// 解析参数列表
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
}
if (constructorToUse == null) {
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
/*
* 确定构造方法参数数量,比如下面的配置:
*
*
*
*
*
*
* 此时 minNrOfArgs = maxIndex + 1 = 2 + 1 = 3,除了计算 minNrOfArgs,
* 下面的方法还会将 cargs 中的参数数据转存到 resolvedValues 中
*/
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 获取构造方法列表
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);
}
}
// 按照构造方法的访问权限级别和参数数量进行排序
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set> ambiguousConstructors = null;
LinkedList causes = null;
for (Constructor candidate : candidates) {
Class[] paramTypes = candidate.getParameterTypes();
/*
* 下面的 if 分支的用途是:若匹配到到合适的构造方法了,提前结束 for 循环
* constructorToUse != null 这个条件比较好理解,下面分析一下条件 argsToUse.length > paramTypes.length:
* 前面说到 AutowireUtils.sortConstructors(candidates) 用于对构造方法进行
* 排序,排序规则如下:
* 1. 具有 public 访问权限的构造方法排在非 public 构造方法前
* 2. 参数数量多的构造方法排在前面
*
* 假设现在有一组构造方法按照上面的排序规则进行排序,排序结果如下(省略参数名称):
*
* 1. public Hello(Object, Object, Object)
* 2. public Hello(Object, Object)
* 3. public Hello(Object)
* 4. protected Hello(Integer, Object, Object, Object)
* 5. protected Hello(Integer, Object, Object)
* 6. protected Hello(Integer, Object)
*
* argsToUse = [num1, obj2],可以匹配上的构造方法2和构造方法6。由于构造方法2有
* 更高的访问权限,所以没理由不选他(尽管后者在参数类型上更加匹配)。由于构造方法3
* 参数数量 < argsToUse.length,参数数量上不匹配,也不应该选。所以
* argsToUse.length > paramTypes.length 这个条件用途是:在条件
* constructorToUse != null 成立的情况下,通过判断参数数量与参数值数量
* (argsToUse.length)是否一致,来决定是否提前终止构造方法匹配逻辑。
*/
if (constructorToUse != null && argsToUse.length > paramTypes.length) {
break;
}
/*
* 构造方法参数数量低于配置的参数数量,则忽略当前构造方法,并重试。比如
* argsToUse = [obj1, obj2, obj3, obj4],上面的构造方法列表中,
* 构造方法1、2和3显然不是合适选择,忽略之。
*/
if (paramTypes.length < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
if (resolvedValues != null) {
try {
/*
* 判断否则方法是否有 ConstructorProperties 注解,若有,则取注解中的
* 值。比如下面的代码:
*
* public class Persion {
* private String name;
* private Integer age;
*
* @ConstructorProperties(value = {"coolblog", "20"})
* public Persion(String name, Integer age) {
* this.name = name;
* this.age = age;
* }
* }
*/
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
/*
* 获取构造方法参数名称列表,比如有这样一个构造方法:
* public Person(String name, int age, String sex)
*
* 调用 getParameterNames 方法返回 paramNames = [name, age, sex]
*/
paramNames = pnd.getParameterNames(candidate);
}
}
/*
* 创建参数值列表,返回 argsHolder 会包含进行类型转换后的参数值,比如下
* 面的配置:
*
*
*
*
*
*
*
* Person 的成员变量 age 是 Integer 类型的,但由于在 Spring 配置中
* 只能配成 String 类型,所以这里要进行类型转换。
*/
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring);
}
catch (UnsatisfiedDependencyException ex) {
if (this.beanFactory.logger.isTraceEnabled()) {
this.beanFactory.logger.trace(
"Ignoring constructor [" + candidate + "] of bean "" + beanName + "": " + ex);
}
if (causes == null) {
causes = new LinkedList();
}
causes.add(ex);
continue;
}
}
else {
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
/*
* 计算参数值(argsHolder.arguments)每个参数类型与构造方法参数列表
* (paramTypes)中参数的类型差异量,差异量越大表明参数类型差异越大。参数类型差异
* 越大,表明当前构造方法并不是一个最合适的候选项。引入差异量(typeDiffWeight)
* 变量目的:是将候选构造方法的参数列表类型与参数值列表类型的差异进行量化,通过量化
* 后的数值筛选出最合适的构造方法。
*
* 讲完差异量,再来说说 mbd.isLenientConstructorResolution() 条件。
* 官方的解释是:返回构造方法的解析模式,有宽松模式(lenient mode)和严格模式
* (strict mode)两种类型可选。具体的细节没去研究,就不多说了。
*/
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
/*
* 如果两个构造方法与参数值类型列表之间的差异量一致,那么这两个方法都可以作为
* 候选项,这个时候就出现歧义了,这里先把有歧义的构造方法放入
* ambiguousConstructors 集合中
*/
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
// 若上面未能筛选出合适的构造方法,这里将抛出 BeanCreationException 异常
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)");
}
/*
* 如果 constructorToUse != null,且 ambiguousConstructors 也不为空,表明解析
* 出了多个的合适的构造方法,此时就出现歧义了。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);
}
if (explicitArgs == null) {
/*
* 缓存相关信息,比如:
* 1. 已解析出的构造方法对象 resolvedConstructorOrFactoryMethod
* 2. 构造方法参数列表是否已解析标志 constructorArgumentsResolved
* 3. 参数值列表 resolvedConstructorArguments 或 preparedConstructorArguments
*
* 这些信息可用在其他地方,用于进行快捷判断
*/
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
try {
Object beanInstance;
if (System.getSecurityManager() != null) {
final Constructor ctorToUse = constructorToUse;
final Object[] argumentsToUse = argsToUse;
beanInstance = AccessController.doPrivileged(new PrivilegedAction