Spring推断构造器源码分析

        Spring中bean虽然可以通过多种方式(Supplier接口、FactoryMethod、构造器)创建bean的实例对象,但是使用最多的还是通过构造器创建对象实例,也是我们最熟悉的创建对象的方式。如果有多个构造器时,那Spring是如何推断使用哪个构造器来创建bean对象实例的?

一、Spring中创建对象实例的方式

(1) 通过构造器实例化对象

        这是我们创建实例最常使用的方式,也是最常见的方式。

public class ObjectInstance {private String createMode;public ObjectInstance(String createMode) {System.out.println("create by constructor");this.createMode = createMode;}public ObjectInstance() {}public String getCreateMode() {return createMode;}public void setCreateMode(String createMode) {this.createMode = createMode;}@Overridepublic String toString() {return "ObjectInstance{" +"createMode='" + createMode + '\'' +'}';}
}
<!--通过构造器实例化bean--><bean id="objBean" class="cn.crazy.newInstance.ObjectInstance"><constructor-arg name="createMode" value="create by constructor"></constructor-arg></bean>

(2)通过工厂方法实例化对象

public class CrazyInstanceFactory {//静态工厂方法public static Object newInstanceForStatic(){ObjectInstance objectInstance = new ObjectInstance();objectInstance.setCreateMode("create by factory's static method");return objectInstance;}//实例工厂方法public Object newInstance(){ObjectInstance objectInstance = new ObjectInstance();objectInstance.setCreateMode("create by factory's  instance method");return objectInstance;}
}

     (i)静态工厂方法

<!--通过静态工厂实例化bean--><bean id="objInstanceForStaticFactory" class="cn.crazy.newInstance.CrazyInstanceFactory" factory-method="newInstanceForStatic"></bean>

     (ii)实例工厂方法

<!--通过实例工厂实例化bean--><bean id="crazyInstanceFactory" class="cn.crazy.newInstance.CrazyInstanceFactory"></bean><bean id="objInstance" factory-bean="crazyInstanceFactory" factory-method="newInstance"></bean>

(3)通过实现FactoryBean接口实例化对象

public class CrazyFactoryBean implements FactoryBean<ObjectInstance> {@Overridepublic ObjectInstance getObject() throws Exception {ObjectInstance objectInstance = new ObjectInstance();objectInstance.setCreateMode("create by FactoryBean");return objectInstance;}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {return true;}
}
<!--通过FactoryBean接口实例化bean--><bean id="objInstanceForFactoryBean" class="cn.crazy.newInstance.CrazyFactoryBean"></bean>

(4)通过Supplier实例化对象

public class SupplierObj{private String version;private int num;public SupplierObj() {}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}@Overridepublic String toString() {return "SupplierObj{" +"version='" + version + '\'' +", num=" + num +'}';}
}

        使用BeanFactoryPostProcessor对beanDefinition信息进行修改,为其通过一个Supplier,用于实例化bean。

         Supplier接口只有一个方法,我们可以用简化语法创建匿名接口实现类。

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}
public class MyPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanFactory.getBeanDefinition("supplierObj");//beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue();beanDefinition.setInstanceSupplier(() -> {SupplierObj supplierObj1 = new SupplierObj();return supplierObj1;});}
}
<bean id="supplierObj" class="cn.crazy.newInstance.SupplierObj"><property name="num" value="12"></property><property name="version" value="1.0.0"></property></bean>

二、推断构造器

(1)doCreateBean(),创建bean实例对象。

          doCreateBean()方法是Spring进行Bean创建的核心方法,它包含三个参数:

  • String beanName,就是bean的id或anme;
  • RootBeanDefinition mbd,Spring Bean在没有被创建之前,bean的信息(xml配置bean及注解注入的信息)会被解析成BeanDefinition对象,我们知道Class是描述Java对象信息的,那么BeanDefinition就是描述Spring Bean信息的;
  • Object[] args,构造器参数。这个值默认为null,除非程序员自己调用api传入参数。

        这个方法中会调用多次后置处理器,用于推断构造器、完成BD合并、解决循环依赖、解析注解等。我们今天主要看关于自动装配相关的内容,所以给这个方法省略了一些内容。

        首先是推断构造器,通过合适的构造器创建bean的实例,然后对实例进行属性填充。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//第二次调用后置处理器的入口方法(推断构造方法)//代码块一:创建实例beaninstanceWrapper = createBeanInstance(beanName, mbd, args);}……//填充属性//调用第五次和第六次后置处理器的地方populateBean(beanName, mbd, instanceWrapper);return exposedObject;}

  (2) createBeanInstance(),根据特定的bean及实例化策略创建一个bean实例。

        实例化一个bean的方式包括Supplier接口、FactoryMethod、构造器等。

        spring会缓存一些关于对象创建中比较复杂的操作,方便在原型下bean创建可以直接获取到对象创建的方式,而不是又进行复杂的推断。

        resolved表示创建对象的构造方法有没有被解析过 。为什么要有这个标签?

        方便spring快速的创建对象 ,因为创建对象时,推断构造方法是个很复杂的过程,在创建原型bean时,可以只推断一次,后续不用在执行这个复杂的操作 。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.Class<?> beanClass = resolveBeanClass(mbd, beanName);//判断beanClass是否不符合要求if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}//这段代码的含义是?spring提供了一个Supplier接口,用于实例化beanSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// Shortcut when re-creating the same bean...//resolved表示创建对象的构造方法有没有被解析过/*** 为什么要有这个标签?* 方便spring快速的创建对象* 因为创建对象时,推断构造方法是个很复杂的过程,在创建原型bean时,可以只推断一次,后续不用在执行这个复杂的操作*/boolean resolved = false;//构造函数参数是否被解析过boolean autowireNecessary = false;//args是goGetBean(……,args,……)的入参,spring自身传入为nullif (args == null) {synchronized (mbd.constructorArgumentLock) {//表示已经找到了创建对象的方式if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}//单例情况下不会执行这个判断,原型时会用到if (resolved) {if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {return instantiateBean(beanName, mbd);}}//推断构造方法,有多个构造方法是,推断出最合适的,如果只有一个构造方法,返回null(不用推断)// Candidate constructors for autowiring?//第二次调用后置处理器//代码块二:推断构造方法Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);//如果自动注入的方式为构造器注入,会再一次推断构造方法if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {//代码三:确定构造方法参数的值return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// No special handling: simply use no-arg constructor.return instantiateBean(beanName, mbd);}

 (3)代码块二:determineCandidateConstructors(),推断构造器

         推断构造器是通过一个后置处理器AutowiredAnnotationBeanPostProcessor来完成的,这个后置处理器是Spring启动时自添加的。

@Override@Nullablepublic Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)throws BeanCreationException {// 省略lookup methods检查//先从缓存中拿,拿不到再进行推断// Quick check on the concurrent map first, with minimal locking.Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);if (candidateConstructors == null) {// Fully synchronized resolution now...synchronized (this.candidateConstructorsCache) {candidateConstructors = this.candidateConstructorsCache.get(beanClass);if (candidateConstructors == null) {Constructor<?>[] rawCandidates;try {//getDeclared拿到所有类型的(public、protected、private),但不会拿父类的//拿到所有的构造器rawCandidates = beanClass.getDeclaredConstructors();}catch (Throwable ex) {throw new BeanCreationException(beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);Constructor<?> requiredConstructor = null;Constructor<?> defaultConstructor = null;//没有使用到kotlin时永远返回nullConstructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);int nonSyntheticConstructors = 0;for (Constructor<?> candidate : rawCandidates) {if (!candidate.isSynthetic()) {nonSyntheticConstructors++;}else if (primaryConstructor != null) {continue;}MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);if (ann == null) {//beanClass是个CGLIB代理类,找到父类Class<?> userClass = ClassUtils.getUserClass(beanClass);if (userClass != beanClass) {try {Constructor<?> superCtor =userClass.getDeclaredConstructor(candidate.getParameterTypes());ann = findAutowiredAnnotation(superCtor);}catch (NoSuchMethodException ex) {// Simply proceed, no equivalent superclass constructor found...}}}if (ann != null) {//已经有@Autowired(required=true)标记的构造器时,如果再出现@Autowired标记的构造器时会抛出异常if (requiredConstructor != null) {throw new BeanCreationException(beanName,"Invalid autowire-marked constructor: " + candidate +". Found constructor with 'required' Autowired annotation already: " +requiredConstructor);}boolean required = determineRequiredStatus(ann);if (required) {//有多个加了构造器@Autowired或@Value注解if (!candidates.isEmpty()) {throw new BeanCreationException(beanName,"Invalid autowire-marked constructors: " + candidates +". Found constructor with 'required' Autowired annotation: " +candidate);}//@Autowired(required=true)的构造器才会设置为requiredConstructorrequiredConstructor = candidate;}//只有添加了构造方法上加了@Autowired或@Value注解,该构造方法都会放入candidatescandidates.add(candidate);}else if (candidate.getParameterCount() == 0) {//defaultConstructor设置为无参构造器defaultConstructor = candidate;}}if (!candidates.isEmpty()) {// Add default constructor to list of optional constructors, as fallback.if (requiredConstructor == null) {if (defaultConstructor != null) {//如果candidates不能空,且无确定的构造方法,添加默认的构造方法到可选列表中candidates.add(defaultConstructor);}else if (candidates.size() == 1 && logger.isInfoEnabled()) {logger.info("Inconsistent constructor declaration on bean with name '" + beanName +"': single autowire-marked constructor flagged as optional - " +"this constructor is effectively required since there is no " +"default constructor to fall back to: " + candidates.get(0));}}//将candidates转换为构造器数组candidateConstructors = candidates.toArray(new Constructor<?>[0]);}//仅当一个有参构造方法时,才会返回这个构造方法else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {candidateConstructors = new Constructor<?>[] {rawCandidates[0]};}// primaryConstructor永远为nullelse if (nonSyntheticConstructors == 2 && primaryConstructor != null &&defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};}else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {candidateConstructors = new Constructor<?>[] {primaryConstructor};}else {//空构造器数组candidateConstructors = new Constructor<?>[0];}this.candidateConstructorsCache.put(beanClass, candidateConstructors);}}}return (candidateConstructors.length > 0 ? candidateConstructors : null);}

这个方法大致总结为:

1)先从缓存中获取候选构造器数组, 如果能获取成功,判断数组长度是否大于0,大于0则返回这个构造器数组,否则返回null。不存在缓存则进行步骤2。

2)获取beanClass中的所有类型的构造器(public、protected、private),存放到rawCandidates数组。创建一个存放候选构造器的List集合candidates,长度为 rawCandidates数组的长度,创建三个构造器类型的变量requiredConstructor(存储@Autowired(required = true)标记的构造器)、defaultConstructor(存储默认的构造器)和primaryConstructor(没有使用到kotlin时永远返回null

),创建一个int类型的变量nonSyntheticConstructors,用于记录beanClass非合成构造器的个数。

3)遍历rawCandidates数组,如果当前的构造器对象为非合成构造器nonSyntheticConstructors计数器+1。定义一个MergedAnnotation类型变量ann,存放当前构造器的Autowired注解信息,如果当前beanClass的ann为null,判断beanClass是否为CGLIB代理类,如果是,将父类相同(参数类型相同)构造器上的Autowired注解信息赋值给ann。

 (a)如果注解信息ann不为null

        (i)判断requiredConstructor是否为null,如果不为null,说明beanClass中已经存在一个@Autowired(required=true)的构造器,抛出异常!

       (ii)获取当前构造器上的注解信息ann的required属性值,如果为true,如果候选构造器的List集合candidates不为空,抛出异常。否则将当前构造器赋值给requiredConstructor变量。

        (iii)将当前构造器加入到候选构造器的List集合candidates中。

 (b)  如果注解信息ann为null

        如果当前构造器为无参构造器,将当前构造器赋值给defaultConstructor变量。

4)完成遍历后,有如下几种情况

 (a)candidates不为空的情况,如果requiredConstructor为null,defaultConstructor不为null,将defaultConstructor加入候选构造器集合candidates中。将candidates转换为构造器数组对象。

(b)rawCandidates数组长度为1且其中的构造器参数个数大于0(仅当一个有参构造方法时,才会返回这个构造方法),将这个构造器对象封装成构造器数组对象。

(c)nonSyntheticConstructors==2(beanClass只有两个非合成构造器)且primaryConstructor不为null(没有使用到kotlin时永远返回null),将primaryConstructor和defaultConstructor封装成构造器数组对象。

  (d)nonSyntheticConstructors==1且primaryConstructor不为null(没有使用到kotlin时永远返回null),将primaryConstructorr封装成构造器数组对象。

  (e)不是以上情况,返回空构造器数组对象。

5)将构造器数组对象缓存到spring容器中,并返回这个数组对象。

(3)代码块三:autowireConstructor(),确定构造器及构造器方法参数的值,完成对象实例化。

        通过推断构造器方法只是推断出合适的构造器,但是可能存在多个候选构造器的情况,而且构造器的参数值也还没有确定,接下来就是确定构造方法参数的值,并完成对象的实例化。

{
……
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);//如果自动注入的方式为构造器注入,会再一次推断构造方法if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {//确定构造方法参数的值return autowireConstructor(beanName, mbd, ctors, args);}……
}protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);}
//这里只是简单初始化
public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) {this.beanFactory = beanFactory;this.logger = beanFactory.getLogger();}

 这里定义了很多中间变量,如:

//最终使用的构造方法
Constructor<?> constructorToUse = null;
//最终使用的参数持有者,维护了参数差异值比较的方法
ArgumentsHolder argsHolderToUse = null;
//最终使用的构造方法参数
Object[] argsToUse = null;
//这个值默认为null,除非程序员自己调用api传入参数
//createBean(beanName, mbd, args);中args即是explicitArgs
Object[] explicitArgs;
//解析出来的构造器参数值
//解析出来的参数不一定就是可以直接注入的属性,需要进行转换
//如<constructor-arg  type="" ></constructor-arg>或如<constructor-arg  ref="" ></constructor-arg>
//需要转换成具体的Bean才能注入
Object[] argsToResolve = null;
//最小参数个数,如果手动设置构造器参数,createBean(beanName, mbd, args)或
//xml配置了<constructor-arg></constructor-arg>
//或设置了BeanDefinition.getConstructorArgumentValues().addArgumentValues();
//最小参数个数就是设置参数的个数
int minNrOfArgs;
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;//构造器参数。这个值默认为null,除非程序员自己调用api传入参数//createBean(beanName, mbd, args);中args即是explicitArgsif (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) {//构造器中没有缓存构造参数,就需要获取preparedConstructorArguments//部分准备好的构造函数参数,不是最终使用的构造器参数,需要转换argsToResolve = mbd.preparedConstructorArguments;}}}if (argsToResolve != null) {//解析出来的参数不一定就是可以直接注入的属性,需要进行转换//如<constructor-arg  type="" ></constructor-arg>或如<constructor-arg  ref="" ></constructor-arg>//需要转换成具体的Bean才能注入argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);}}if (constructorToUse == null || argsToUse == null) {// Take specified constructors, if any.Constructor<?>[] candidates = chosenCtors;if (candidates == null) {Class<?> beanClass = mbd.getBeanClass();try {//拿出所有的构造方法// mbd.isNonPublicAccessAllowed() == true,是否允许非public修饰的构造器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;}//通过无参构造器实例化beanbw.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;//最小参数个数int minNrOfArgs;//实际传入构造方法参数个数不为nullif (explicitArgs != null) {//如果指定了构造器参数值,最小参数个数为指定的参数个数minNrOfArgs = explicitArgs.length;}else {//xml配置了<constructor-arg></constructor-arg>//或设置了BeanDefinition.getConstructorArgumentValues().addArgumentValues();//就根据手动设置的构造器参数推断出所需要的构造器ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();//找出构造方法的参数个数,并根据cargs构建ConstructorArgumentValues对象minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}//代码块四:对candidates进行排序AutowireUtils.sortConstructors(candidates);//计算权重int minTypeDiffWeight = Integer.MAX_VALUE;//存放摸棱两可的构造器Set<Constructor<?>> ambiguousConstructors = null;Deque<UnsatisfiedDependencyException> causes = null;for (Constructor<?> candidate : candidates) {int parameterCount = candidate.getParameterCount();//因为进行了排序,如果当前的parameterCount<argsToUse.length,那么后续candidate.parameterCount总是小于argsToUse.lengthif (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 = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);if (paramNames == null) {ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {paramNames = pnd.getParameterNames(candidate);}}//根据构造方法参数名数组和构造器参数值构建argsHolder//这个方法是遍历构造器的参数名字和参数类型,找到与之匹配的resolvedValues中的ValueHolder值,并封装成ArgumentsHolder对象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);}//计算差异值,当多个构造方法的参数个数相同时,通过差异值决定使用哪个构造方法//mbd.isLenientConstructorResolution()是否为宽松模式,默认是宽松模式int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this constructor if it represents the closest match.//如果当前构造器的差异值比minTypeDiffWeight小,将当前构造器的差异值赋值给minTypeDiffWeight变量if (typeDiffWeight < minTypeDiffWeight) {//最终使用的构造器constructorToUse赋值为当前构造器constructorToUse = candidate;//最终使用的参数持有者argsHolderToUse赋值为当前的argsHolderargsHolderToUse = argsHolder;//最终使用的构造器参数argsToUse赋值为当前的argsHolder的argumentsargsToUse = 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);}}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 " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");}//如果存在模棱两可的构造器且不是宽松模式,抛出异常else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous constructor matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousConstructors);}if (explicitArgs == null && argsHolderToUse != null) {//代码块五:将推断出来的构造方法信息缓存到BD中argsHolderToUse.storeCache(mbd, constructorToUse);}}Assert.state(argsToUse != null, "Unresolved constructor arguments");//根据推断出来的构造器及其参数创建bean实例,并封装到bw对象中bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));return bw;}

这个方法大致总结为:

        1)先后bd缓存中获取构造函数的相关信息(构造器、构造参数),如果缓存是部分准备好的构造函数参数,需要转换成最终可使用的构造参数。

        2)缓存中获取构造器或构造参数失败,有如下两种情况:

                a.通过determineCandidateConstructors()推断出来的构造器不为null,直接将推断出来的构造器存放到构造器数组candidates。

                b.通过determineCandidateConstructors()推断出来的构造器为null,拿到当前beanClass中的所有构造器,存放到构造器数组candidates。

        3)如果candidates中只有一个构造器且为无参构造,将该构造器信息缓存到bd中,同时通过这个无参构造器创建bean实例对象,并封装到BeanWrapperImpl中返回。

        4)设置构造器最小参数值minNrOfArgs。如果手动设置了构造器参数,最小参数值就为设置的参数的个数。(通过createBean(beanName, mbd, args)api设置args或者通过xml中<constructor-arg>标签设置。或者是BeanDefinition.getConstructorArgumentValues().addArgumentValues()/BeanDefinition.getConstructorArgumentValues().addIndexedArgumentValue()方式设置)获取bd中的构造参数值,并封装成ConstructorArgumentValues对象(resolvedValues)。

        5)对candidates中的构造器进行排序。创建构造器差异值变量minTypeDiffWeight,并将其赋值为Integer.MAX_VALUE(构造器差异值最小的即为最优解);创建 ambiguousConstructors 变量用于存放模棱两可的构造器。

        6)遍历candidates,获取当前构造器的参数个数parameterCount,如果上一个回合已经推断出constructorToUse和argsToUse,且argsToUse的长度大于当前构造器的参数个数,直接退出遍历。(因为进行了排序,如果当前的parameterCount<argsToUse.length,那么后续candidate.parameterCount可能总是小于argsToUse.length,虽然可能存在修饰符不同的情况,但是这里排序修饰符比构造参数个数优先级高)。在遍历的代码块中有一个重要的逻辑就是计算差异值,而计算差异值的前提是两个构造器的构造参数个数相同。如果当前构造器比前一个构造器的构造参数个数少,是没有资格进行差异值比较的。

        7)如果当前构造器的个数parameterCount 小于最小构造器参数个数minNrOfArgs,跳过本次循环,进行下次循环(pubulic修饰的构造器不满足,可能protected或private修饰的构造器满足)。

        8)构建参数持有者ArgumentsHolder argsHolder,获取当前构造器的构造器参数类型数组Class<?>[] paramTypes。

        (i)resolvedValues值不为空。获取当前构造器参数名字数组。遍历构造器的参数名字和参数类型数组,找到与之匹配的resolvedValues中的ValueHolder值,并封装成ArgumentsHolder对象。

        (ii)explicitArgs不为空,直接通过explicitArgs构建ArgumentsHolder对象。

        9)计算差异值,当多个构造方法的参数个数相同时,通过差异值决定使用哪个构造方法。

        (i)如果当前构造器的差异值比minTypeDiffWeight小,将当前构造器的差异值赋值给minTypeDiffWeight变量,并把最终使用的构造器constructorToUse赋值为当前构造器 constructorToUse = candidate; 最终使用的参数持有者argsHolderToUse赋值为当前的argsHolder argsHolderToUse = argsHolder; 最终使用的构造器参数argsToUse赋值为当前的argsHolder的arguments。同时将ambiguousConstructors值设置为null.

        (ii) 如果当前构造器的差异值与minTypeDiffWeig相等,就会将它放入ambiguousConstructors(如果为null,先创建ambiguousConstructors)中。

        10)如果存在模棱两可的构造器且不是宽松模式下,抛出异常。严格模式下,spring认为不能存在摸棱两可的构造器,而且宽松模式和严格模式下差异值计算的算法也不一样。spring默认为宽松模式。

        11)将推断出来的构造方法信息缓存到BD中,并根据推断出来的构造器及其参数创建bean实例,并封装到bw对象中返回。

(i)代码块四:构造器排序规则

        构造器的排序规则。public修饰的构造器排在前面,如果同为public类型,比较构造器的参数个数,参数个数多的排在前面。

	public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) -> {int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));//为什么在自动注入方式为构造方法注入时,会选择构造方法参数比较多的那个构造方法return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());};
(ii)代码块五:storeCache(),将推断出来的构造方法信息缓存到BD中
//将推断出来的构造方法信息缓存到BD中public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;mbd.constructorArgumentsResolved = true;if (this.resolveNecessary) {mbd.preparedConstructorArguments = this.preparedArguments;}else {mbd.resolvedConstructorArguments = this.arguments;}}}}

三、总结

        spring推断构造方法分如下几种情况:

 (1)不使用自动装配时。

        1)无构造方法或只有一个无参构造方法的情况下,返回null

        2)有且只有一个有参构造方法时,返回这个有参构造方法

        3)有多个构造方法时,返回null

(2)使用@Autowired注入时.

        1)如果只有一个构造方法添加了@Autowired(required=true),返回这个构造方法

        2)如果有一个构造方法添加了@Autowired(required=true),其他构造方法再添加@Autowired注解,抛出异常

        3)如果有多个构造方法添加了@Autowired(required=false),除了返回所以加了@Autowired(required=false)的构造方法,如果提供了无参构造方法,也会返回无参构造方法,后续继续推断构造方法。

(3)使用自动注入时。

        根据构造方法的参数类型或参数个数找到最优的构造方法,默认最优的为参数类型为spring中依赖最多,且参数个数最长的那个。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/103559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Error]在Swift项目Build Settings的Preprocessor Macros中定义的宏无效的问题

问题 如图&#xff0c;在Build Settings -> Preprocessor Macros中添加了ISADEMO1。但在代码中判断无效&#xff0c;还是会输出“isn’t ADemo” #if ISADEMOprint("is ADemo") #elseprint("isnt ADemo") #endif解决 如图&#xff0c;要让Preproces…

学习编程-先改变心态

编程失败的天才 林一和我很久以前就认识了——我从五年级就认识他了。他是班上最聪明的孩子。如果每个人在家庭作业或考试准备方面需要帮助&#xff0c;他们都会去那里。 有趣的是&#xff0c;林一不是那种连续学习几个小时的孩子。 他的聪明才智似乎与生俱来&#xff0c;几乎毫…

机器学习(21)---召回率(recall)、精度(precision)和准确率(accuracy)

文章目录 1. 分布不平衡的数据集2. TP、TN 、FP 、FN3. 混淆矩阵4. 各自的计算公式5. 例题应用 1. 分布不平衡的数据集 1. 精度&#xff08;precision&#xff09;和召回率&#xff08;recall&#xff09;是衡量机器学习模型性能的重要指标&#xff0c;特别是数据集分布不平衡的…

Maven系列第3篇:详解maven解决依赖问题

maven系列目标&#xff1a;从入门开始开始掌握一个高级开发所需要的maven技能。 这是maven系列第3篇。 我们先来回顾一下什么是maven&#xff1f; maven是apache软件基金会组织维护的一款自动化构件工具&#xff0c;专注服务于java平台的项目构件和依赖管理。 本文主要内容…

python字符串中的\“

data {"text": "\"abc\""} print(data) # {"text": ""abc""}从结果可以看到并没有出现反斜杠&#xff0c;反斜杠与双引号作为一个整体&#xff0c;转义为了一个双引号&#xff0c;如果要在字符串中出现反斜杠&am…

使用任务定时执行软件的定时关机功能,控制电脑可用时间段

目录 定时关机功能可以设置有效的时间段 控制电脑可用时间段的意义 定时执行软件介绍 - 定时执行专家 定时关机设置方法 不可用时间段设置方法 注意事项 总结 在现代社会&#xff0c;电脑已经成为人们生活和工作中不可或缺的一部分。但是&#xff0c;长时间使用电脑也会对…

Torch生成类激活图CAM

import torch from torch.nn import functional as F from torchvision import models, transforms from PIL import Image import os os.environ[KMP_DUPLICATE_LIB_OK]TRUE# 加载经过训练的 ResNet 模型 model models.resnet50(pretrainedTrue) model.eval()# 载入图像并进行…

【AI】深度学习——前馈神经网络——卷积神经网络

文章目录 1.2 卷积神经网络1.2.1 卷积一维卷积近似微分低通滤波器/高通滤波器卷积变种 二维卷积卷积的核心就是翻转相乘卷积应用于图像处理 互相关互相关代替卷积 卷积与互相关的交换性 1.2.2 卷积神经网络卷积代替全连接卷积层特征映射卷积层结构参数数量 汇聚层(池化层)汇聚层…

Chrome 118 版本中的新功能

Google Chrome 的最新版本V118正式版 2023/10/10 发布&#xff0c;以下是新版本中的相关新功能供参考。 本文翻译自 New in Chrome 118&#xff0c;作者&#xff1a; Adriana Jara&#xff0c; 略有删改。 以下是主要内容&#xff1a; 使用scope css规则在组件中指定特定样式。…

Mybatis 实现简单增删改查

目录 前言 一、Mybatis是什么 二、配置Mybatis环境 三、创建数据库和表 四、添加业务代码 4.1、添加实体类 4.2、添加mapper接口 4.3、添加实现接口方法的xml文件 五、简单的增删改查操作及单元测试 5.1、单元测试 单元测试具体步骤&#xff1a; 单元测试如何才能不污…

微信小程序动态海报

参考文献&#xff1a; 微信小程序生成分享海报&#xff08;附带二维码生成&#xff09; - 简书 需求背景&#xff1a; 微信小程序固定图片&#xff0c;无法自动链接&#xff0c;分享页面内容 解决方案&#xff1a; 拆分海报内容&#xff0c;由以下几个组成 1、用户图像 …

好莱坞编剧大罢工终于结束;与OpenAI创始人共进早餐;使用DALL-E 3制作绘本分享;生成式AI的基础设施架构 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f525; 好莱坞编剧大罢工终于结束&#xff1a;简单说就是AI妥协了 https://www.wgacontract2023.org/the-campaign/summary-of-the-2023-wga-…

【Python爬虫 js渲染思路一】

Python爬虫 破解js渲染思路一 当我们在谈论网页js渲染的时候&#xff0c;我们在谈论什么 js渲染网页&#xff0c;从某种程度来说&#xff0c;是指单纯的http请求&#xff0c;返回的文本数据&#xff0c;与我们在浏览器看到的内容&#xff0c;相距甚远.其可包括为以下几点&…

C++的高手之旅

要学习C并成为C大佬&#xff0c;以下是一些建议&#xff1a; 掌握C基础知识&#xff1a;C是一种面向对象的编程语言&#xff0c;它包含了C语言的大部分语法和特性。因此&#xff0c;学习C之前&#xff0c;建议先掌握C语言的基础知识&#xff0c;包括数据类型、控制流、指针、内…

微调Yolov8动物姿势估计模型

本文主要以狗的姿势估计为例,展示如何对当下流行的YOLOv8姿势模型进行Fine-tuning,并附录完整代码。 动物姿势估计是计算机视觉领域的一个研究方向,它是人工智能的一个子领域,专注于自动检测和分析图像或视频中动物的姿势和位置。其目标是确定一个或多个动物身体部位的空间…

msvcr120.dll丢失怎样修复?总结msvcr120.dll丢失的5修复方法

在使用计算机的过程中&#xff0c;我们常常会遇到各种问题&#xff0c;其中之一就是“计算机丢失msvcr120.dll丢失的困扰”。这个问题可能对一些人来说并不陌生&#xff0c;但是对于初次遇到这个问题的人来说&#xff0c;可能会感到无所适从。因此&#xff0c;小编将详细探讨这…

免费开源的非标项目型制造BOM一键导入方案介绍

非标项目型制造&#xff0c;每一个订单都会引入很多新料号、新BoM、新工艺路线。实施ERP/MES系统&#xff0c;实现生产管理数字化&#xff0c;第一步就是要导入这些料号、BoM和工艺。项目型制造&#xff0c;大多数订单只生产一次。但在ERP/MES系统中&#xff0c;订单的料号、Bo…

React 注意事项

在使用 React 进行开发时&#xff0c;有一些注意事项可以帮助你更好地使用这个JavaScript库。以下是一些需要注意的事项&#xff1a; 组件结构和组织 尽量保持组件简单和可复用&#xff1a;将组件拆分为较小和独立的部分&#xff0c;以提高代码的可维护性和可测试性。遵循单一…

解决:Package ‘setuptools‘ requires a different Python: 3.7.16 not in ‘>=3.8‘

#conda install setuptools pip install --upgrade "setuptools<65" Collecting setuptools<65 Downloading setuptools-64.0.3-py3-none-any.whl (1.2 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1…

【Unity】【VR】如何让Distance Grab抓取物品时限制物品的Rotation

【背景】 遇到这样的场景,希望抓取Canvas时,Canvas不会沿Z轴旋转。 【问题】 发现Freeze Canvas的Rigid Body没有用。 【分析】 应该是RigidBody的限制仅在物理互动下生效,抓取可能不属于物理互动(比如碰撞),所以不生效。 【思路】 还是得写脚本挂载在Interacta…