简介
很多时候我们的构造器都不止一个,那么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,那么直接实例化instantiateConstructor<?> 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也至少得是 3ConstructorArgumentValues 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;}// 如果参数个数小于要求的参数个数,passif (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) {// 如果根据当前的方法参数计算出来的评分更小些,那么应该使用该构造方法来创建BeanconstructorToUse = 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会根据评分算法帮我们找出方法
至于其中的方法评分的算法,不是重点,大概就是说匹配成都越高的分越低,分越低就优先级越高,如果匹配度不高那么是要加分的,比如当前构造参数类型是值的类型的父类,加两分,当前类型是个接口加一分
关于这个算法,后面出文章讲解
欢迎关注,学习不迷路!