ConstructorResolver.autowireConstructor()源码分析

概述

ConstructorResolver 是 Spring 框架中的一个类,用于解析和选择合适的构造函数来实例化 bean 对象。
在 Spring 中,当需要实例化一个 bean 时,需要确定使用哪个构造函数来创建对象。ConstructorResolver 类负责执行这个任务,并选择适合的构造函数。

通过使用 ConstructorResolver,Spring 能够智能地选择适合的构造函数来实例化 bean,并进行自动装配,以满足依赖关系。

标题

ConstructorResolver 类的主要方法是 autowireConstructor(),其签名如下:

public BeanWrapper autowireConstructor(
    String beanName, 
    RootBeanDefinition bd, 
    Constructor<?>[] chosenCtors, 
    Object[] explicitArgs
) {
    // ...
}

autowireConstructor() 方法接受以下参数:

  • beanName:要实例化的 bean 的名称。
  • bd:bean 的根定义对象 RootBeanDefinition。
  • chosenCtors:候选的构造函数数组,表示可能的构造函数选择。
  • explicitArgs:显式指定的构造函数参数,用于在自动装配时提供明确的参数。

autowireConstructor() 方法的主要任务是根据给定的参数选择合适的构造函数,并使用这个构造函数来实例化 bean 对象。它会考虑以下因素

  • 自动装配:如果构造函数上有 @Autowired@Inject 注解,ConstructorResolver 将尝试自动装配构造函数参数。
  • 候选构造函数:ConstructorResolver 将根据候选的构造函数数组 chosenCtors来选择最佳的构造函数。候选构造函数通常是通过解析 bean 类的所有构造函数得到的。
  • 显式参数:如果在explicitArgs中提供了显式参数,则ConstructorResolver将使用这些参数来调用构造函数。

autowireConstructor() 方法返回一个BeanWrapper对象,其中包含了实例化后的 bean 对象和其相关的属性。

源码分析

先明确一些概念

mbd.getConstructorArgumentValues()方法


在 Spring 框架中,mbd.getConstructorArgumentValues() 方法用于获取 bean 的构造函数参数值。
mbd 是 RootBeanDefinition 类的一个实例,它代表了一个 bean 的根定义。通过调用 mbd.getConstructorArgumentValues() 方法,可以获取到 bean 的构造函数参数值的信息。


具体来说,getConstructorArgumentValues() 方法返回一个 ConstructorArgumentValues 对象,它包含了构造函数参数的相关信息。ConstructorArgumentValues 类提供了多种方法来操作构造函数参数值,例如:
- addIndexedArgumentValue(int index, Object value):向构造函数参数列表中添加一个索引参数值。
- addGenericArgumentValue(Object value):向构造函数参数列表中添加一个通用参数值。
- getIndexedArgumentValue(int index, Class<?> requiredType):获取指定索引位置的参数值,并根据需要的类型进行类型转换。


通过 mbd.getConstructorArgumentValues() 方法获取到的 ConstructorArgumentValues 对象,可以对构造函数参数进行增加、获取和修改等操作。
在 Spring 的 XML 配置中,可以使用 constructor-arg 元素来定义构造函数参数值。mbd.getConstructorArgumentValues() 方法的作用就是获取这些定义的构造函数参数值,并提供给 Spring 容器使用。
总的来说,mbd.getConstructorArgumentValues() 方法的作用是获取 bean 的构造函数参数值,并提供对这些参数值的操作方法。

ArgumentsHolder类是什么?


在 Spring 框架中,ArgumentsHolder 是一个用于保存构造函数参数的辅助类。它通常在构造函数解析和实例化过程中使用。


ArgumentsHolder 类主要用于封装构造函数的参数值和参数类型信息。它提供了对参数值和类型的访问方法,以便在构造函数解析期间进行处理。


在 Spring 的源代码中,ArgumentsHolder 类的定义可能会有所不同,具体实现可能会根据不同的版本和上下文而有所变化。

ConstructorPropertiesChecker是什么?


在 Spring 框架中,ConstructorPropertiesChecker 是一个用于检查构造函数参数和注解的辅助类。它主要用于处理 @ConstructorProperties 注解。


@ConstructorProperties 注解是 Java 标准库中的注解,用于指定构造函数参数的名称。它通常用于在没有源代码可用的情况下,通过参数名称来匹配构造函数参数。Spring 框架使用 ConstructorPropertiesChecker 类来检查和处理带有 @ConstructorProperties 注解的构造函数。


ConstructorPropertiesChecker 类的主要功能是验证构造函数的参数名称是否与 @ConstructorProperties 注解中指定的名称一致。如果一致,它将使用注解中指定的名称作为参数名称,否则将使用默认的参数名称。


通过使用 @ConstructorProperties 注解和 ConstructorPropertiesChecker 类,Spring 可以更准确地解析构造函数参数,并在实例化 bean 对象时正确地匹配参数。

/**
 * "autowire constructor" (with constructor arguments by type) behavior.
 * Also applied if explicit constructor argument values are specified,
 * matching all remaining arguments with beans from the bean factory.
 * <p>This corresponds to constructor injection: In this mode, a Spring
 * bean factory is able to host components that expect constructor-based
 * dependency resolution.
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param chosenCtors chosen candidate constructors (or {@code null} if none)
 * @param explicitArgs argument values passed in programmatically via the getBean method,
 * or {@code null} if none (-> use constructor argument values from bean definition)
 * @return a BeanWrapper for the new instance
 */
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
      @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
      
   // chosenCtors:候选的构造函数,有可能为空
   // explicitArgs:明确的参数,从getBean()方法调用本方法的时候传入的参数

   BeanWrapperImpl bw = new BeanWrapperImpl();
   this.beanFactory.initBeanWrapper(bw);

   // 创建几个参数后面要用
   // 表示要使用的构造函数
   Constructor<?> constructorToUse = null;
   ArgumentsHolder argsHolderToUse = null;
   // 表示要使用的参数
   Object[] argsToUse = null;

   // 如果已经提供了精确的参数,则直接使用这些参数来匹配构造函数
   if (explicitArgs != null) {
      argsToUse = explicitArgs;
   }
   else {
      // 如果没有明确指定构造函数的参数,则先从bd的缓存中找
      Object[] argsToResolve = null;
      // 缓存上锁
      synchronized (mbd.constructorArgumentLock) {
         // 从缓存中获取
         constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
         if (constructorToUse != null && mbd.constructorArgumentsResolved) {
            // Found a cached constructor...
            // 从缓存中获取解析好的参数
            argsToUse = mbd.resolvedConstructorArguments;
            if (argsToUse == null) {
               // 如果还没有解析好的参数,获取尚未完全解析好的参数
               argsToResolve = mbd.preparedConstructorArguments;
            }
         }
      }
      
      // 由于参数可能尚未解析完全,所以,再解析一遍
      if (argsToResolve != null) {
         argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
      }
   }

   // 不知道该使用哪一个构造函数和哪些参数的时候走这里面的处理逻辑
   if (constructorToUse == null || argsToUse == null) {
      // Take specified constructors, if any.
      // 如果候选列表不为空,则从候选列表里面找
      Constructor<?>[] candidates = chosenCtors;
      // 如果候选列表为空
      if (candidates == null) {
         // 通过反射获取bean的所有构造函数
         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);
         }
      }

      // 如果只有一个构造函数,而且没有明确指定参数
      if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
         Constructor<?> uniqueCandidate = candidates[0];
         // 判断是否这一个唯一的构造函数,是不是默认无参构造函数
         if (uniqueCandidate.getParameterCount() == 0) {
            synchronized (mbd.constructorArgumentLock) {
               // 更改缓存中的内容
               mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
               mbd.constructorArgumentsResolved = true;
               mbd.resolvedConstructorArguments = EMPTY_ARGS;
            }
            // 直接使用无参构造函数并返回结果
            bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
            return bw;
         }
      }

      // Need to resolve the constructor.
      // 这里确实需要去解析构造函数了
      boolean autowiring = (chosenCtors != null ||
            mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
      ConstructorArgumentValues resolvedValues = null;

      // 设置最小参数个数,可以控制 Spring 在选择构造函数时的灵活性
      int minNrOfArgs;
      if (explicitArgs != null) {
         minNrOfArgs = explicitArgs.length;
      }
      else {
         // 从beanDefintion中获取构造函数的参数值,
         // 通常来说,mbd.getConstructorArgumentValues()中的参数是来自于xml配置或注解配置中
         ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
         resolvedValues = new ConstructorArgumentValues();
         minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
      }

      // 寻找最匹配的构造函数
      // 排序,根据构造函数参数数量降序排序,public的优先
      AutowireUtils.sortConstructors(candidates);
      int minTypeDiffWeight = Integer.MAX_VALUE;
      Set<Constructor<?>> ambiguousConstructors = null;
      Deque<UnsatisfiedDependencyException> causes = null;

      // 这里的候选构造函数,要么是来自于传入的候选列表,要么是通过反射获取的该类所有构造函数
      // 逐个遍历,查看候选构造函数的匹配度
      for (Constructor<?> candidate : candidates) {
         int parameterCount = candidate.getParameterCount();

         if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
            // Already found greedy constructor that can be satisfied ->
            // do not look any further, there are only less greedy constructors left.
            // 已经找到了最匹配的构造函数了,不用再寻找其他的了。
            // 上面有一步是通过缓存来获取待使用的构造函数和参数,如果待使用参数的数量大于当前构造函数的参数数量
            // 则可以终止循环了
            break;
         }
         // 如果当前构造函数的参数数量小于指定的最小值,直接排除
         if (parameterCount < minNrOfArgs) {
            continue;
         }

         ArgumentsHolder argsHolder;
         Class<?>[] paramTypes = candidate.getParameterTypes();
         if (resolvedValues != null) {
            try {
               String[] paramNames = null;
               if (resolvedValues.containsNamedArgument()) {
                  // 检查和处理带有 @ConstructorProperties 注解的构造函数
                  paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
                  
                  // paramNames为空,则说明没有使用@ConstructorProperties注解
                  if (paramNames == null) {
                     // 获取参数名称探索器
                     ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                     // 如果能获取名称探索器,就用名称探索器获取参数名称列表
                     if (pnd != null) {
                        paramNames = pnd.getParameterNames(candidate);
                     }
                  }
               }
               // 基于参数类型列表和参数名列表来创建argsHolder
               argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                     getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
            }
            catch (UnsatisfiedDependencyException ex) {
               // 异常处理逻辑,不赘述了
               if (logger.isTraceEnabled()) {
                  logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
               }
               // Swallow and try next constructor.
               if (causes == null) {
                  causes = new ArrayDeque<>(1);
               }
               causes.add(ex);
               continue;
            }
         }
         else {
            // resolvedValues 为空的情况下
            // Explicit arguments given -> arguments length must match exactly.
            // 给定的显式参数->参数长度必须完全匹配。
            if (parameterCount != explicitArgs.length) {
               continue;
            }
            // 在构造函数没有提供参数列表的时候,使用explicitArgs创建一个argsHolder 
            argsHolder = new ArgumentsHolder(explicitArgs);
         }

         // 计算差异权重 值越小,说明差异越小
         int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
               argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
         // Choose this constructor if it represents the closest match.
         // 如果此构造函数表示最接近的匹配,选择它。
         if (typeDiffWeight < minTypeDiffWeight) {
            constructorToUse = candidate;
            argsHolderToUse = argsHolder;
            argsToUse = argsHolder.arguments;
            minTypeDiffWeight = typeDiffWeight;
            ambiguousConstructors = null;
         }
         else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
            // 如果已经找到候选构造函数,且当前这个构造函数也有相同的类似度则保存到 ambiguousConstructors 中。后面用于抛出异常
            if (ambiguousConstructors == null) {
               ambiguousConstructors = new LinkedHashSet<>();
               ambiguousConstructors.add(constructorToUse);
            }
            ambiguousConstructors.add(candidate);
         }
      }

      // 如果到这一步,constructorToUse变量依然为空,则说明找不到合适的构造函数了
      // 后面的逻辑都是用于抛出异常的
      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 on bean class [" + mbd.getBeanClassName() + "] " +
               "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities. " +
               "You should also check the consistency of arguments when mixing indexed and named arguments, " +
               "especially in case of bean definition inheritance)");
      }
      else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Ambiguous constructor matches found on bean class [" + mbd.getBeanClassName() + "] " +
               "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
               ambiguousConstructors);
      }
      
      //异常抛出完毕

      // 将解析的构造函数加入缓存
      if (explicitArgs == null && argsHolderToUse != null) {
         argsHolderToUse.storeCache(mbd, constructorToUse);
      }
   }

   // 实例化bean,封装为beanWrapper返回
   Assert.state(argsToUse != null, "Unresolved constructor arguments");
   bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
   return bw;
}

源码很长,整理一下整个方法的流程。

  1. 首先判断explicitArgs,如果不为空,那就使用explicitArgs中的参数直接去找相应的构造函数,也就是说,使用的构造函数的参数,就是explicitArgs中的元素。
  2. 如果explicitArgs为空,就尝试从缓存中获取,也就是RootBeanDefinition中的resolvedConstructorArgumentspreparedConstructorArguments中获取构造函数参数,前者代表解析完毕的参数,后者代表尚未解析完的参数。如果参数尚未解析完,那么需要再来一轮解析。
  3. 如果缓存列表也是空的,去检查参数中的候选列表,从候选列表里面选,如果形参中的候选列表也为空,那就调用jdk反射,获取所有构造函数列表。
  4. 如果到这一步的候选列表长度为1,那就直接调用。
  5. 如果候选列表长度超过1,则需要先排序,然后进行构造函数的选举。
    a. 这里的排序规则是,public的构造函数排在前面,按照参数数量降序排。然后是非public的构造函数,按照参数数量降序排。
  6. 排序完成后,遍历构造函数列表,找差异性最小的构造函数进行bean的实例化

将这六步流程进一步概括,概括出来就是以下几个环节。

    1. 解析构造函数的参数
    1. 获取候选构造函数集合
    1. 解析各个构造函数的参数个数
    1. 寻找差异值最小的构造函数

第一步,解析构造函数的参数,这个其实没有什么需要特别多说的,按照explicitArgs—>缓存—>解析参数的顺序来获取。
如果explicitArgs不为空,那么argsToUse属性直接就是explicitArgs

如果explicitArgs为空,则尝试从mbd.resolvedConstructorArguments中获取解析完毕的参数,如果不为空且mbd.preparedConstructorArguments为空,那么argsToUse就是mbd.resolvedConstructorArguments

如果mbd.preparedConstructorArguments不为空,说明有尚未解析完毕的构造函数参数,则需要进一步解析,调用resolvePreparedArguments()方法,并将结果赋值给argsToUse

第一步并不很复杂。



第二步,获取候选构造函数集合,这也不难理解,就是调用这个方法的时候,spring已经提前知道了可能的构造函数列表,相对于这个bean所有的构造函数,已经缩小了范围。

如果chosenCtors不为空,candidates = chosenCtors。如果chosenCtors为空,通过反射获取这个bean所有的构造函数列表。即candidates = beanClass.getDeclaredConstructors()或beanClass.getConstructors()

这里需要注意的是,从外部传进来的chosenCtors,也可能是空的。

getDeclaredConstructors()和getConstructors()的区别

getConstructors()方法只能获取public的权限的构造方法。
getDeclaredConstructors()方法可以获取该类所有的构造函数。



第三步,获取minNrOfArgs,即最小参数个数,当然,也是最终选择的构造函数的入参个数。
minNrOfArgs 的作用是在 Spring 容器实例化 bean 时,根据构造函数的参数数量来选择合适的构造函数进行实例化。如果指定了 minNrOfArgs 属性,Spring 将只考虑那些参数数量大于或等于 minNrOfArgs 的构造函数,而忽略参数数量小于 minNrOfArgs 的构造函数。
通过设置 minNrOfArgs 属性,可以实现对构造函数的选择和过滤,以满足特定的需求。例如,如果希望只使用具有特定参数数量的构造函数来创建 bean 实例,可以设置 minNrOfArgs 属性为相应的值。
如果explicitArgs是不为空的,那么minNrOfArgs就是explicitArgs的长度。如果explicitArgs为空,则调用resolveConstructorArguments()来获取minNrOfArgs的值。

关于resolveConstructorArguments()的分析在这篇文章里面
spring源码学习 resolveConstructorArguments()方法

这一步一方面获取了minNrOfArgs,另一方面,也填充了resolvedValues



第四步,寻找匹配度最高的构造函数,并使用instantiate()方法去实例化bean。

在遍历candidates的时候,总的来说,spring采用的是一个类似贪心算法的策略,筛选会经过以下几个过程。

  1. 参数个数匹配,首先会检查候选构造函数的参数个数是否与需要自动装配的参数个数相匹配。只有参数个数匹配的构造函数才会被考虑。parameterCount小于minNrOfArgs则直接跳过,parameterCount小于argsToUseconstructorToUse 不为空,则可以确定使用的构造函数与参数。
  2. 参数类型匹配,如果参数个数匹配,会进一步检查每个参数的类型是否与所需的参数类型相匹配。这里使用的是 Spring 的类型匹配算法,可以处理继承关系、泛型等特殊情况。首先获取paramTypes,然后这里会根据resolvedValues是否为空来判断代码走哪个分支。
  3. 如果resolvedValues不为空,表示已经解析了一部分构造函数参数。这时候会检查一下是否存在按名称获取的构造函数参数,如果有,先尝试使用@ConstructorProperties注解来获取参数名,如果获取不到,就通过ParameterNameDiscoverer来获取。然后通过paramTypesparamNames来获取构造函数参数的完整信息并装入argsHolder
  4. 如果resolvedValues为空,首先判断parameterCount != explicitArgs.length,如果为true,直接排除当前candidate构造函数,如果相同,使用explicitArgs创建argsHolder
  5. 计算差异权重,差异权重的值越小,说明差异越小。具体的计算逻辑这里不详述了,spring会根据参数类型的继承关系,接口关系,以及精确匹配来进行评估。当评估结果小于minTypeDiffWeight的时候,就会将当前candidate的内容赋值给constructorToUseargsToUse等,并更新minTypeDiffWeight


第五步,spring已经尽力使用了所有的方法去匹配待使用的构造函数,如果仍无法确定使用哪一个构造函数,那么就要抛出异常了。如果已经确定使用哪一个构造函数了,那么使用instantiate()方法来实例化bean。

总结

ConstructorResolver 是 Spring 框架中的一个类,用于解析和选择合适的构造函数来实例化 bean 对象。spring提供了很多种方法来初始化bean中的参数(属性注入)。ConstructorResolver要解决的主要问题就是从配置文件或注解中读取并解析bean的属性值,例如从xml配置文件中的<constructor-arg>标签中读取属性值。但是<constructor-arg>中的属性配置是非常自由的,可以不提供属性名,也不提供属性的下标索引。但是自由的同时也对spring创建bean时候属性注入造成了困难。因此spring也需要提供一套复杂的逻辑来完成bean的创建。
对于spring来说,调用无参构造函数创建一个bean是非常容易的,但调用有参构造函数,则困难度与复杂度有了明显的提升。spring首先要确认参数数量,然后确认参数的类型与名称,然后再去匹配最合适的构造函数,并实例化bean。
不过对于现在的springboot的时代来说,日常使用中,应该不会再有这么粗犷的写法了吧,哈哈。

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值