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.