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);