很多时候我们的构造器都不止一个,那么spring怎么选择的呢,签名介绍了推断构造方法的扩展点,可以使用@Autowired注解去选择使用哪个构造器,但是即使这样也有可能有多个Autowired且required为false的构造器,那么还是得选择
前面我们介绍过了@Bean的实例化,其实推断构造器的逻辑与其相差不多。
源码在 org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor
// chosenCtors 指定使用哪几个构造器,explicitArgs 参数
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
// 设置一些类型转换器等
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
if (explicitArgs != null) { // 已经指定了构造方法参数
argsToUse = explicitArgs;
}
else {
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) { // 一般都进入
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
// 进入这表示没有确定要使用哪个构造器,那么拿到该类的所有构造器放到candidates候选
Class<?> beanClass = mbd.getBeanClass();
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
// 构造器只有一个,没有指定构造器参数,也没有预先设置constructorArgumentValues,那么直接实例化instantiate
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;
}
}
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
// 要选择构造器参数最多的,如果小于这个值那么pass,如果大于更新
int minNrOfArgs;
if (explicitArgs != null) {
// 如果指定传入了参数值,那么minNrOfArgs不能低于传入的长度
minNrOfArgs = explicitArgs.length;
}
else {
// 这种方式指定构造器的值有点特殊,前面文章也介绍过
// 它可以指定参数下标的值,比如指定了0,2那么表示指定了第一个参数和第三个参数的值,虽然指定参数只有 2个,但是minNrOfArgs也至少得是 3
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 构造方法进行排序
// public的方法排在最前面
// 都是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) {
// 如果说已经选出来要用的构造器和入参对象,但是指定的入参比当前构造器参数还多,那么直接break,因为排了序,后面参数肯定更少
break;
}
// 如果参数个数小于要求的参数个数,pass
if (parameterCount < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
Class<?>[] paramTypes = candidate.getParameterTypes();
if (resolvedValues != null) {
// resolvedValues有值那么explicitArgs肯定就没值,所以进入这里是因为BeanDefinition指定了参数
try {
// 如果在构造方法上使用了@ConstructorProperties,那么就直接取定义的value作为构造方法的参数名
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
// 找出参数名称,反射 & 本地变量表
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
// 根据BeanDefinition中定义的参数,以及通过name从beanFactory获取到Bean
// 最终组成为argsHolder ,这里面的详细过程后面文章讲
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
if (causes == null) {
causes = new ArrayDeque<>(1);
}
// 记录异常,后面推断不出方法便抛出异常
causes.add(ex);
continue;
}
}
else {
// resolvedValues为null,那么explicitArgs就一定有值
// 通过getBean传入的,那么参数个数必须一致
if (parameterCount != explicitArgs.length) {
continue;
}
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) {
// 如果根据当前的方法参数计算出来的评分更小些,那么应该使用该构造方法来创建Bean
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null; // 同时也就不存在模棱两可的方法了
}
// 如果评分一样的,那么表示推断不出使用哪个方法构造Bean,最终找不出来要抛异常
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
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)");
}
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
// 表示推断不出使用哪个方法构造Bean,抛异常
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);
}
}
// 通过反射调用uniqueCandidate返回一个对象,然后设置到BeanWrapper返回
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
以上便是推断构造方法的关键逻辑,因为构造方法可能有多个,这个时候如果指定了参数,那么直接根据指定的参数匹配方法,如果没有指定参数,那么spring会根据评分算法帮我们找出方法
至于其中的方法评分的算法,不是重点,大概就是说匹配成都越高的分越低,分越低就优先级越高,如果匹配度不高那么是要加分的,比如当前构造参数类型是值的类型的父类,加两分,当前类型是个接口加一分
关于这个算法,后面出文章讲解
欢迎关注,学习不迷路!