This is the 14th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021.”

【 1 】 autowireConstructor

protected BeanWrapper autowireConstructor(

      String beanName, RootBeanDefinition mbd, @NullableConstructor<? >[] ctors,@Nullable Object[] explicitArgs) {

   return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
Copy the code

The previous constructor is just a constructor. The important thing is the following:

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition MBD, //ctors: fetch one of these methods //args: getBean, manually passed arguments@NullableConstructor<? >[] chosenCtors,@Nullable Object[] explicitArgs) {

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

    // Finally selected constructor and corresponding parametersConstructor<? > constructorToUse =null;
   ArgumentsHolder argsHolderToUse = null;
   Object[] argsToUse = null;

   if(explicitArgs ! =null) {
       // If args is specified, it is constructed with the specified parameters
      argsToUse = explicitArgs;
   }
   else {
      Object[] argsToResolve = null;
       // The constructor has no cache
      synchronized(mbd.constructorArgumentLock) { constructorToUse = (Constructor<? >) mbd.resolvedConstructorOrFactoryMethod;/ / a cache
         if(constructorToUse ! =null && mbd.constructorArgumentsResolved) {
            // Found a cached constructor...
            argsToUse = mbd.resolvedConstructorArguments;
            if (argsToUse == null) {
                // RuntimeBeanReference [T(1)]argsToResolve = mbd.preparedConstructorArguments; }}}if(argsToResolve ! =null) {
         // Where [T(1)] above is resolved hereargsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); }}// At least one of the two is unknown, that is, there is no way to construct the class example at this time
   if (constructorToUse == null || argsToUse == null) { Constructor<? >[] candidates = chosenCtors;// If no constructor was given before
      if (candidates == null) { Class<? > beanClass = mbd.getBeanClass();try {
             // Depending on the bd configuration, take the public constructor, or all constructors
            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 there is only one constructor to choose from, and no parameters are specified manually, and no values are passed in BD
      if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
          // Is the constructor parameterless?Constructor<? > uniqueCandidate = candidates[0];
          // It is not a parameter, so it is directly constructed
         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));
            returnbw; }}// There are several constructors, one of which will be needed to construct the example below
      booleanautowiring = (chosenCtors ! =null ||
            mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
      ConstructorArgumentValues resolvedValues = null;

       // This variable, which refers to the number of arguments less than this, will not be used to construct the example
      int minNrOfArgs;
       // The parameters here are passed in from the previous manual getBean
      if(explicitArgs ! =null) {
         minNrOfArgs = explicitArgs.length;
      }
      else {
          // This refers to the number of parameters manually specified by BD
         ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
         resolvedValues = new ConstructorArgumentValues();
         minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
      }

       // The sort is public first, then the number of arguments is reversed
      AutowireUtils.sortConstructors(candidates);
       // This parameter refers to the constructor score: even if we determine a constructor, the number of arguments may be the same, and we need to determine which one to choose in some way
      intminTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<? >> ambiguousConstructors =null;
      Deque<UnsatisfiedDependencyException> causes = null;

       // Iterate over the sorted constructor above
      for(Constructor<? > candidate : candidates) {int parameterCount = candidate.getParameterCount();

          // The current constructor has fewer arguments than minNrOfArgs, so there is no need to check
         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;
         }
           // A constructor has not been selected yet, and the number of arguments is less than minNrOfArgs: the private constructor needs to be considered at this point, so continue
         if (parameterCount < minNrOfArgs) {
            continue; } ArgumentsHolder argsHolder; Class<? >[] paramTypes = candidate.getParameterTypes();if(resolvedValues ! =null) {
            try {
                [1.1]
               String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
               if (paramNames == null) {
                   // Get the name of the argument in the code
                  ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                  if(pnd ! =null) { paramNames = pnd.getParameterNames(candidate); }}// Find the corresponding bean object according to the parameter type and name [1.2]
               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 {
            // Explicit arguments given -> arguments length must match exactly.
            if(parameterCount ! = explicitArgs.length) {continue;
            }
            argsHolder = new ArgumentsHolder(explicitArgs);
         }
		// Look, there will be a score to confirm the constructor [1.3]
         int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
               argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
         // Choose this constructor if it represents the closest match.
          // Look, here will calculate the score
          // The rule is: equal scores do not change, small scores will change, which means that the first one found under the same score has a higher probability of execution
         if (typeDiffWeight < minTypeDiffWeight) {
            constructorToUse = candidate;
            argsHolderToUse = argsHolder;
            argsToUse = argsHolder.arguments;
            minTypeDiffWeight = typeDiffWeight;
            ambiguousConstructors = null;
         }
          // If the score is equal, the conflict needs to be recorded, and if the process still exists at the end, an exception will be thrown
         else if(constructorToUse ! =null && typeDiffWeight == minTypeDiffWeight) {
            if (ambiguousConstructors == null) {
               ambiguousConstructors = newLinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); }}// No constructor was found
       // Why? It is possible that a parameter lookup bean is not found (for example, multiple lookup beans are found by class, and then by name, but the name is not found), and will be stored before
      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)");
      }
       // If there are multiple constructors with the same score and MBD is not loose, an error is reported
      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);
      }
		// The cache will be stored according to the previous constructor
      if (explicitArgs == null&& argsHolderToUse ! =null) { argsHolderToUse.storeCache(mbd, constructorToUse); }}// The bean example will be constructed hereAssert.state(argsToUse ! =null."Unresolved constructor arguments");
   bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
   return bw;
}
Copy the code

[1.1] ConstructorProperties

We can specify the parameter alias through this annotation on the constructor:

@ConstructorProperties({"1","2","3",... })
Copy the code

【 1.2 】 createArgumentArray

private ArgumentsHolder createArgumentArray(
      String beanName, RootBeanDefinition mbd, @NullableConstructorArgumentValues resolvedValues, BeanWrapper bw, Class<? >[] paramTypes,@Nullable String[] paramNames, Executable executable,
      boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {

   TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); TypeConverter converter = (customConverter ! =null ? customConverter : bw);

   ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
   Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
   Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// Iterate over the constructor's argument types
   for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
       // Look at the parameter type and nameClass<? > paramType = paramTypes[paramIndex]; String paramName = (paramNames ! =null ? paramNames[paramIndex] : "");
      // Try to find matching constructor argument value, either indexed or generic.
      ConstructorArgumentValues.ValueHolder valueHolder = null;
      if(resolvedValues ! =null) {
         valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
         // 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 || paramTypes.length == resolvedValues.getArgumentCount())) { valueHolder = resolvedValues.getGenericArgumentValue(null.null, usedValueHolders); }}if(valueHolder ! =null) {
         // We found a potential match - let's give it a try.
         // Do not consider the same value definition multiple times!
         usedValueHolders.add(valueHolder);
         Object originalValue = valueHolder.getValue();
         Object convertedValue;
         if (valueHolder.isConverted()) {
            convertedValue = valueHolder.getConvertedValue();
            args.preparedArguments[paramIndex] = convertedValue;
         }
         else {
            MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
            try {
               convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
            }
            catch (TypeMismatchException ex) {
               throw new UnsatisfiedDependencyException(
                     mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
                     "Could not convert argument value of type [" +
                           ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
                           "] to required type [" + paramType.getName() + "]." + ex.getMessage());
            }
            Object sourceHolder = valueHolder.getSource();
            if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
               Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
               args.resolveNecessary = true;
               args.preparedArguments[paramIndex] = sourceValue;
            }
         }
         args.arguments[paramIndex] = convertedValue;
         args.rawArguments[paramIndex] = originalValue;
      }
      else {
         MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
         // No explicit match found: we're either supposed to autowire or
         // have to fail creating an argument array for the given constructor.
         if(! autowiring) {throw new UnsatisfiedDependencyException(
                  mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
                  "Ambiguous argument values for parameter of type [" + paramType.getName() +
                  "] - did you specify the correct bean references as arguments?");
         }
         try {
             // If an argument is not specified and autowired is true
            // The resolveDependency method in dependency injection is called to try to assign a bean object from the container by type and name
            Object autowiredArgument = resolveAutowiredArgument(
                  methodParam, beanName, autowiredBeanNames, converter, fallback);
             // Save it for the external constructor to use
            args.rawArguments[paramIndex] = autowiredArgument;
            args.arguments[paramIndex] = autowiredArgument;
            args.preparedArguments[paramIndex] = autowiredArgumentMarker;
            args.resolveNecessary = true;
         }
         catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(
                  mbd.getResourceDescription(), beanName, newInjectionPoint(methodParam), ex); }}}for (String autowiredBeanName : autowiredBeanNames) {
      this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
      if (logger.isDebugEnabled()) {
         logger.debug("Autowiring by type from bean name '" + beanName +
               "' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
               " to bean named '" + autowiredBeanName + "'"); }}return args;
}
Copy the code

[1.3] Score calculation

int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
      argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
Copy the code

As you can see, there are still loose and non-loose modes:

Loose mode:

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.
    // here: arguments stands for found and converted arguments; RawArguments mean: arguments found
   int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
   int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
   return Math.min(rawTypeDiffWeight, typeDiffWeight);
}
Copy the code

And here it is:

public static int getTypeDifferenceWeight(Class
       [] paramTypes, Object[] args) {
   int result = 0;
   for (int i = 0; i < paramTypes.length; i++) {
       // If it doesn't match, return a large number: it doesn't match anyway
      if(! ClassUtils.isAssignableValue(paramTypes[i], args[i])) {return Integer.MAX_VALUE;
      }
      if(args[i] ! =null) {
          // See here: if there is inheritance, each inheritance differs by 2 points until the inherited class is the same as the class specified by the argumentClass<? > 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; }}// Add a point if it is an interface
         if (paramType.isInterface()) {
            result = result + 1; }}}return result;
}
Copy the code

Strict mode:

public int getAssignabilityWeight(Class
       [] paramTypes) {
    // The strict mode is based on inheritance, but instead directly checks to see if the found parameter values match the specified values
   for (int i = 0; i < paramTypes.length; i++) {
       // If there is a mismatch, return directly
      if(! ClassUtils.isAssignableValue(paramTypes[i],this.arguments[i])) {
         returnInteger.MAX_VALUE; }}// A mismatch occurred in the match
   for (int i = 0; i < paramTypes.length; i++) {
      if(! ClassUtils.isAssignableValue(paramTypes[i],this.rawArguments[i])) {
         return Integer.MAX_VALUE - 512; }}// If all matches, return this number
   return Integer.MAX_VALUE - 1024;
}
Copy the code

Strict mode has a lot less judgment and only three return values, in which case optimal conflicts are more likely to occur.