Spring 重点难点源码分析: autowireConstructor

explicitArgs及缓存的用法,略过不表

主要逻辑:

这个方法要根据xml配置文件中惟一的bean配置找到合适的构造方法(可能有多个)并返回实例换包装类,

因此一开始定义两个变量

此处为用来实例化的类的构造函数
Constructor<?> constructorToUse = null;
此为构造函数参数配置(包含xml中配置的构造函数参数值)
ArgumentsHolder argsHolderToUse = null;

首先,根据bean定义配置计算构造函数至少有几个参数

minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
resolveConstructorArguments的原理是先计算bean定义配置中 constructor-arg中的参数总数, 然后再遍历所有带index的constructor-arg,如果有哪个index比参数总数大,那就把minNrOfArgs置为index + 1,因为index是从0开始的。

minNrOfArgs 的计算为后面寻找合适的构造方法做了准备,因为如果参数数量小于bean定义中配置的参数数量,那么肯定是不合适的构造函数。

然后,我们取出所有的可用构造函数

candidates = (mbd.isNonPublicAccessAllowed() ?
      beanClass.getDeclaredConstructors() : beanClass.getConstructors());
排序后 AutowireUtils.sortConstructors(candidates);

再遍历

for (int i = 0; i < candidates.length; i++) {

如果参数数量小于bean定义中配置的参数数量,不考虑
if (paramTypes.length < minNrOfArgs) {
   continue;
}

对于每个我们创建 

ArgumentsHolder argsHolder;
argsHolder = createArgumentArray(
      beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
createArgumentArray中则对候选构造方法的所有类型进行遍历
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
先到根据index到bean定义中去找,如果找不到,根据参数名称与bean定义中的contructor-arg中的名称相同
或者 参数类型与bean定义中的contructor-arg中的参数类型相同 去找到合适的参数定义
// Try to find matching constructor argument value, either indexed or generic.
ConstructorArgumentValues.ValueHolder valueHolder =
      resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);

如果还是找不到,则要把bean定义中的contructor-arg中的只定义了value,未定义type,name的取出来作为参数定义。
(当然,如果一个构造方法3个参数,如果第一个参数已经选用了这个未定义type,name的参数定义,那么第二,第三个参数则不能再选,任何参数定义只被选一次,最后创建成参数定义数组)
// 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).
if (valueHolder == null && !autowiring) {
   valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
}

从bean定义类 ConstructorArgumentValues.ValueHolder 转换到 ArgumentsHolder args,中间需要转换一下,比如bean定义类中只放置了bean定义里配的参数,字符串2。 而ArgumentsHolder则需要配置 实际构造方法要使用的值,如果类型是数值,则要进行转换,放到 其 preparedArguments数组中待用。

 

对每一个构造方法,我们获取到了合适的bean定义值并放入argsHolder后,还要进行类型比较判断是否合适。

此处我们只看严格模式。

int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
      argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
public int getTypeDifferenceWeight(Class<?>[] paramTypes) {
   // If valid arguments found, determine type difference weight.
   // Try type difference weight on both the converted arguments and
   // the raw arguments. If the raw weight is better, use it.
   // Decrease raw weight by 1024 to prefer it over equal converted weight.
   int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
   int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
   return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
}
public static int getTypeDifferenceWeight(Class<?>[] paramTypes, Object[] args) {
   int result = 0;
   for (int i = 0; i < paramTypes.length; i++) {
      if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
         return Integer.MAX_VALUE;
      }
      if (args[i] != null) {
         Class<?> paramType = paramTypes[i];
         Class<?> superClass = args[i].getClass().getSuperclass();
         while (superClass != null) {
            if (paramType.equals(superClass)) {
               result = result + 2;
               superClass = null;
            }
            else if (ClassUtils.isAssignable(paramType, superClass)) {
               result = result + 2;
               superClass = superClass.getSuperclass();
            }
            else {
               superClass = null;
            }
         }
         if (paramType.isInterface()) {
            result = result + 1;
         }
      }
   }
   return result;
}

由上面可以看到,逐个比较参数类型,如果值的类型不是构造方法中参数类型的子类,直接返回 Integer.MAX_VALUE,最大重量,也就是直接不匹配。如果构造方法中的类型是实际值类型的父类则+2,如果构造方法类型是实际值 类型的接口,则+1

其算法大约如下   

如果构造函数参数类型为接口,则  +1, 如果非接口,则为(构造函数参数类型辈分 - 实际值类型辈分)* 2

最后只有算出的重量值小于int minTypeDiffWeight = Integer.MAX_VALUE;才被选用。

if (typeDiffWeight < minTypeDiffWeight) {
   constructorToUse = candidate;
   argsHolderToUse = argsHolder;
   argsToUse = argsHolder.arguments;
   minTypeDiffWeight = typeDiffWeight;
   ambiguousConstructors = null;
}

在选出了合适的构造方法及参数定义后,实例化包装类

beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
      mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值