ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring 实例化--谁是我的候选人

2021-12-04 18:05:13  阅读:174  来源: 互联网

标签:候选 candidate -- Spring paramTypes int 实例 null 构造函数


微信公众号_CoderLi

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

得出的结论

  1. 如果存在 Autowired 修饰的构造函数、且 required 为 true 、那么有且仅有一个被 Autowired 修饰的构造函数。并且只返回其作为候选结果
  2. 如果存在一个或多个被 Autowired 修饰的构造函数、required 肯定都是 false的、这个时候如果声明了默认构造函数、则默认构造函数会加入到候选列表中、整个候选列表作为候选结果。
  3. 如果该类的构造函数都没有使用 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⃣️ 了吗?

https://mp.weixin.qq.com/s/lCEXLQS1hs9W5xCjbL2DCg

标签:候选,candidate,--,Spring,paramTypes,int,实例,null,构造函数
来源: https://www.cnblogs.com/-coder-li/p/15642735.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有