@Service public class HelloService { @Autowired(required = false) public HelloService(ApplicationContext applicationContext) { // 1⃣️ } @Autowired(required = false) public HelloService(Environment environment) { // 2⃣️ } } 复制代码
快告诉我、Spring 将选择哪个构造函数用来实例化 HelloService ? 1⃣️ 还是 2⃣️ ?文末给出答案
Spring 海选一
第一场海选的场地为 : AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
Constructor<?>[] rawCandidates; try { // 反射获取所有构造函数 rawCandidates = beanClass.getDeclaredConstructors(); } catch (Throwable ex) { throw new BeanCreationException(xxxxxxxxx); } // 构造函数候选列表 List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length); // 构造函数种子候选 Constructor<?> requiredConstructor = null; // 默认构造函数 Constructor<?> defaultConstructor = null; // 这里非 kotlin 语言、返回 null Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass); // 非合成构造函数 int nonSyntheticConstructors = 0; for (Constructor<?> candidate : rawCandidates) { if (!candidate.isSynthetic()) // 可以忽略 nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue;// 可以忽略 } // 判断该构造函数是否被 @Autowire 注解修饰 MergedAnnotation<?> ann = findAutowiredAnnotation(candidate); if (ann == null) { Class<?> userClass = ClassUtils.getUserClass(beanClass); if (userClass != beanClass) { try { Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) { // Simply proceed, no equivalent superclass constructor found... } } } // 被注解修饰 if (ann != null) { if (requiredConstructor != null) { // 已经有种子候选了、不允许再有注解修饰的构造函数 throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: " + candidate + ". Found constructor with 'required' Autowired annotation already: " + requiredConstructor); } boolean required = determineRequiredStatus(ann); if (required) { // 当前构造函数为种子候选、同理不允许再有注解修饰的构造函数 if (!candidates.isEmpty()) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: " + candidates + ". Found constructor with 'required' Autowired annotation: " + candidate); } // 成为种子候选 requiredConstructor = candidate; } // 加入到候选列表中 candidates.add(candidate); } else if (candidate.getParameterCount() == 0) { // 成为默认构造函数 defaultConstructor = candidate; } } if (!candidates.isEmpty()) { // 候选列表不为空 if (requiredConstructor == null) { if (defaultConstructor != null) { // 如果种子候选为 null 、并且默认构造函数不为 null、将默认构造函数加入到候选列表中作为后备方案 candidates.add(defaultConstructor); } } candidateConstructors = candidates.toArray(new Constructor<?>[0]); } else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { // 如果候选列表为 null 并且该类只申明了一个构造函数且它存在入参、那么将它加入到候选列表中 candidateConstructors = new Constructor<?>[] {rawCandidates[0]}; } .....省略掉 kotlin 相关的 else { candidateConstructors = new Constructor<?>[0]; } // 加入缓存中 this.candidateConstructorsCache.put(beanClass, candidateConstructors); ........ // 选妃结束 return (candidateConstructors.length > 0 ? candidateConstructors : null); 复制代码
代码不方便看的话、这里列一下几个重要的变量
rawCandidates // 该类声明的所有构造函数 // 候选列表(构造函数) List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length); // 种子候选(构造函数) Constructor<?> requiredConstructor = null; // 默认构造函数 Constructor<?> defaultConstructor = null; 复制代码
得出的结论
- 如果存在 Autowired 修饰的构造函数、且 required 为 true 、那么有且仅有一个被 Autowired 修饰的构造函数。并且只返回其作为候选结果
- 如果存在一个或多个被 Autowired 修饰的构造函数、required 肯定都是 false的、这个时候如果声明了默认构造函数、则默认构造函数会加入到候选列表中、整个候选列表作为候选结果。
- 如果该类的构造函数都没有使用 Autowired 修饰、并且该类只存在一个构造函数、并且不是无参构造函数、那么则将其作为候选结果返回。
所以开篇中的 HelloService 中将会返回两个构造函数作为候选结果
Spring 海选二
第一场海选的场地为 : ConstructorResolver#autowireConstructor
// 冠军构造函数 Constructor<?> constructorToUse = null; // 冠军构造函数的参数 ArgumentsHolder argsHolderToUse = null; // 先按方法修饰符进行排序、public 优先级最高。然后按照参数个数排序、入参个数越多、优先级越高 AutowireUtils.sortConstructors(candidates); // 最小的类型差异权重 int minTypeDiffWeight = Integer.MAX_VALUE; // 模凌两可的构造函数列表 Set<Constructor<?>> ambiguousConstructors = null; // 筛选过程中出现的异常 LinkedList<UnsatisfiedDependencyException> causes = null; // candidates 海选一选出的构造函数 for (Constructor<?> candidate : candidates) { // 入参个数 int parameterCount = candidate.getParameterCount(); // 冠军构造函数已经产生、并且冠军构造函数入参的个数大于当前构造函数的入参个数了、那么就没必要继续往下了 // 因为 Spring 总想给你它能给的最多的爱 if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) { break; } // minNrOfArgs 这里正常都是 0 、可以忽略 if (parameterCount < minNrOfArgs) { continue; } // 尝试从 Spring 中找出构造函数需要的参数、如果找不出、不中断、而是继续下一个构造函数 ArgumentsHolder argsHolder; Class<?>[] paramTypes = candidate.getParameterTypes(); if (resolvedValues != null) { try { String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount); 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) { // Swallow and try next constructor. if (causes == null) { causes = new LinkedList<>(); } // 保存异常、在 Spring 中找不到依赖的构造函数参数 causes.add(ex); continue; } } else { 、 if (parameterCount != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } // 成功从 Spring 中找出构造函数参数、这里去进行比较找出的对象和参数的类型 (类型差异权重) int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // 哪个构造函数的类型差异更小、哪个就能成为冠军构造函数 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { .... 加入到模凌两可的构造函数列表中 ambiguousConstructors.add(candidate); } } if (constructorToUse == null) { // 流选了、冠军构造函数一个都没有 } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { // 因为构造函数创建 bean 使用的宽松模式即 mbd.isLenientConstructorResolution() 一直为true // 所以即使中途出现模凌两可的构造函数列表、也不会抛出异常 throw new BeanCreationException(xxx); } // 使用 constructorToUse, argsToUse 创建 bean bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); 复制代码
代码不方便看的话、这里列一下几个重要的变量
// 冠军构造函数 Constructor<?> constructorToUse = null; // 冠军构造函数的参数 ArgumentsHolder argsHolderToUse = null; // 先按方法修饰符进行排序、public 优先级最高。然后按照参数个数排序、入参个数越多、优先级越高 AutowireUtils.sortConstructors(candidates); // 最小的类型差异权重 int minTypeDiffWeight = Integer.MAX_VALUE; // 模凌两可的构造函数列表 Set<Constructor<?>> ambiguousConstructors = null; 复制代码
回到我们文章开头的问题、ApplicationContext 和 Environment 毫无疑问都能从 BeanFactory 中找出来、那么它的差异权重就成为选择哪个构造函数的关键了。
因为是构造函数实例化 bean、所以采用的是宽松模式、也就是 mbd.isLenientConstructorResolution()
返回的是 true。如果是配置类(也就是 FactoryMethod ) 就是严格模式、也就是返回 false。
int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); 复制代码
arguments 和 rawArguments 转换之后的参数和原始的参数、我们认为它们是一样的在大多数情况下
public int getTypeDifferenceWeight(Class<?>[] paramTypes) { int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments); int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024; return Math.min(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; } 复制代码
我们回到文章的问题、
public int getTypeDifferenceWeight(Class<?>[] paramTypes) { int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments); int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024; return Math.min(rawTypeDiffWeight, typeDiffWeight); } 复制代码
ApplicationContext 从 Spring 中找出来的类型为 AnnotationConfigServletWebServerApplicationContext
result = 2 + 2 + 2 + 2 + 1 = 9 、最终返回结果为 9 - 1024 = -1015
AbstractApplicationContext 的 父类 DefaultResourceLoader 不是 ApplicationContext 的子类或实现、所以终止于此
而 Environment 从 Spring 中找出来的类型为 StandardServletEnvironment
result = 2 + 2 + 1 = 5 最终返回结果为 5 - 1024 = -1019
AbstractEnvironment 的父类为 Object 、没有实现 Environment 接口、所以终止于此。
所以你知道选 1⃣️ 还是 2⃣️ 了吗?