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中依赖最多,且参数个数最长的那个。
相关文章:

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

十五、【历史记录画笔工具组】
文章目录 历史记录画笔工具历史记录艺术画笔工具 历史记录画笔工具 历史记录画笔工具很简单,就是将画笔工具嗯,涂抹过的修改过的地方,然后用历史记录画笔工具重新修改回来,比如我们将三叠美元中的一叠用画笔工具先涂抹掉…...

Spark上使用pandas API快速入门
文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github ;这博客是记录我学习的点点滴滴,如果您对 Python、Java、AI、算法有兴趣,可以关注我的…...

【WebRTC---源码篇】(十:零)WEBRTC/StreamStatisticianImpl持续更新中)
StreamStatisticianImpl是WebRTC的一个内部实现类,用于统计和管理媒体流的各种统计信息。 StreamStatisticianImpl负责记录和计算以下统计数据: 1. 带宽统计:记录媒体流的发送和接收带宽信息,包括发送比特率、接收比特率、发送丢…...

调用Lua脚本tostring(xxx)报attempt to call a nil value (global ‘tostring‘
在c程序里调用Lua脚本, 脚本中用到了转字符串 tostring(xxx) str "test" function output(a,b,c)d "a:"..tostring(a).."b:"..tostring(b).."c"..tostring(c)return d end 实际运行会报错: attempt to call a nil v…...

PBA.客户需求分析 需求管理
一、客户需求分析 1 需求的三个层次: Requirement/Wants/Pains 大部分人认为,产品满足不了客户需要,是因为客户告知的需求是错误的,这听起来有一些道理,却没有任何意义。不同角色对于需求的理解是不一样的。在客户的需求和厂家的…...

Kafka进阶
Kafka进阶 Kafka事务 kafka的事务机制是指kafka支持跨多个主题和分区的原子性写入,即在一个事务中发送的所有消息要么全部成功,要么全部失败。 kafka的事务机制涉及到以下几个方面: 事务生产者(transactional producer&#x…...
大数计算:e^1000/300!
1.问题:大数计算可能超出数据类型范围 当单独计算 ,因为 ,double的最大取值为1.79769e308,所以 肯定超过了double的表示范围。 同样,对于300!也是如此。 那我们应该怎么去计算和存储结果呢?…...

力扣164最大间距
1.前言 因为昨天写了一个基数排序,今天我来写一道用基数排序实现的题解,希望可以帮助你理解基数排序。 这个题本身不难,就是线性时间和线性额外空间(O(n))的算法,有点难实现 基数排序的时间复杂度是O(d*(nradix)),其中…...

聚观早报 | “百度世界2023”即将举办;2024款岚图梦想家上市
【聚观365】10月13日消息 “百度世界2023”即将举办 2024款岚图梦想家上市 腾势D9用户超10万 华为发布新一代GigaGreen Radio OpenAI拟进行重大更新 “百度世界2023”即将举办 “百度世界2023”将于10月17日在北京首钢园举办。届时,百度创始人、董事长兼首席执…...

Windows 应用程序监控重启
执行思路 1.定时关闭可执行程序,2.再通过定时监控启动可执行程序 定时启动关闭程序.bat echo off cd "D:\xxxx\" :: 可执行程序目录 Start "" /b xxxx.exe :: 可执行程序 timeout /T 600 /nobreak >nul :: 600秒 taskkill /IM xxxx.exe /…...

springboot 通过url下载文件并上传到OSS
DEMO流程 传入一个需要下载并上传的url地址下载文件上传文件并返回OSS的url地址 springboot pom文件依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w…...

docker创建elasticsearch、elasticsearch-head部署及简单操作
elasticsearch部署 1 拉取elasticsearch镜像 docker pull elasticsearch:7.7.0 2 创建文件映射路径 mkdir /mydata/elasticsearch/data mkdir /mydata/elasticsearch/plugins mkdir /mydata/elasticsearch/config 3 文件夹授权 chmod 777 /mydata/elastic…...

竞赛选题 深度学习+python+opencv实现动物识别 - 图像识别
文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数:3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 *…...

Codeforces Round 903 (Div. 3)ABCDE
Codeforces Round 903 (Div. 3)ABCDE 目录 A. Dont Try to Count题目大意思路核心代码 B. Three Threadlets题目大意思路核心代码 C. Perfect Square题目大意思路核心代码 D. Divide and Equalize题目大意思路核心代码 E. Block Sequence题目大意思路核心代码 A. Don’t Try t…...

C# 与 C/C++ 的交互
什么是平台调用 (P/Invoke) P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。 托管代码与非托管的区别 托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中,如 mono 或 java 虚拟机等…...

新版Android Studio搜索不到Lombok以及无法安装Lombok插件的问题
前言 在最近新版本的Android Studio中,使用插件时,在插件市场无法找到Lombox Plugin,具体表现如下图所示: 1、操作步骤: (1)打开Android Studio->Settings->Plugins,搜索Lom…...

BST二叉搜索树
文章目录 概述实现创建节点查找节点增加节点查找后驱值根据关键词删除找到树中所有小于key的节点的value 概述 二叉搜索树,它具有以下的特性,树节点具有一个key属性,不同节点之间key是不能重复的,对于任意一个节点,它…...

【Leetcode】211. 添加与搜索单词 - 数据结构设计
一、题目 1、题目描述 请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。 实现词典类 WordDictionary : WordDictionary() 初始化词典对象void addWord(word) 将 word 添加到数据结构中,之后可以对它…...

Discuz户外旅游|旅行游记模板/Discuz!旅行社、旅游行业门户网站模板
价值328的discuz户外旅游|旅行游记模板,本模板需要配套【仁天际-PC模板管理】插件使用。 模板说明 1、模板页面宽度1200px,简洁大气,较适合户外旅行、骑行、游记、摩旅、旅游、活动等类型的论坛、频道网站; 2、所优化的页面有&…...

【重拾C语言】十一、外部数据组织——文件
目录 前言 十一、外部数据组织——文件 11.1 重新考虑户籍管理问题——文件 11.2 文件概述 11.2.1 文件分类 11.2.2 文件指针、标记及文件操作 11.3 打开、关闭文件 11.4 I/O操作 11.4.1 字符读写 11.4.2 字符串读写 11.4.3 格式化读写 11.4.4 数据块读写 11.4.5 …...

dpdk/spdk/网络协议栈/存储/网关开发/网络安全/虚拟化/ 0vS/TRex/dpvs技术专家成长体系教程
课程围绕安全,网络,存储,云原生4个维度去讲解核心技术点。 6个专栏组成:dpdk网络专栏、存储技术专栏、安全与网关开发专栏、虚拟化与云原生专栏、测试工具专栏、性能测试专栏 一、dpdk网络 dpdk基础知识 多队列网卡࿰…...

树莓派玩转openwrt软路由:5.OpenWrt防火墙配置及SSH连接
1、SSH配置 打开System -> Administration,打开SSH Access将Interface配置成unspecified。 如果选中其他的接口表示仅在给定接口上侦听,如果未指定,则在所有接口上侦听。在未指定下,所有的接口均可通过SSH访问认证。 2、防火…...

Gin:获取本机IP,获取访问IP
获取本机IP func GetLocalIP() []string {var ipStr []stringnetInterfaces, err : net.Interfaces()if err ! nil {fmt.Println("net.Interfaces error:", err.Error())return ipStr}for i : 0; i < len(netInterfaces); i {if (netInterfaces[i].Flags & ne…...

缓存降级代码结构设计
缓存降级设计思想 接前文缺陷点 本地探针应该增加计数器,多次异常再设置,避免网络波动造成误判。耦合度过高,远端缓存和本地缓存应该平行关系被设计为上下游关系了。公用的远端缓存的操作方法应该私有化,避免集成方代码误操作&…...

一文深入理解高并发服务器性能优化
我们现在已经搞定了 C10K并发连接问题 ,升级一下,如何支持千万级的并发连接?你可能说,这不可能。你说错了,现在的系统可以支持千万级的并发连接,只不过所使用的那些激进的技术,并不为人所熟悉。…...

pytorch中的归一化函数
在 PyTorch 的 nn 模块中,有一些常见的归一化函数,用于在深度学习模型中进行数据的标准化和归一化。以下是一些常见的归一化函数: nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d: 这些函数用于批量归一化 (Batch Normalization…...

【管理运筹学】第 10 章 | 排队论(1,排队论的基本概念)
文章目录 引言一、基本概念1.1 排队过程1.2 排队系统的组成和特征1.3 排队模型的分类1.4 系统指标1.5 系统状态 引言 开一点排队论的内容吧,方便做题。 排队论(Queuing Theory)也称随机服务系统理论,是为解决一系列排队问题&…...

【Express】服务端渲染(模板引擎 EJS)
EJS(Embedded JavaScript)是一款流行的模板引擎,可以用于在Express中创建动态的HTML页面。它允许在HTML模板中嵌入JavaScript代码,并且能够生成基于数据的动态内容。 下面是一个详细的讲解和示例,演示如何在Express中…...

Linux CentOS8安装gitlab_ce步骤
1 下载安装包 wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/8/gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm/download.rpm2 安装gitlab yum install policycoreutils-python-utilsrpm -Uvh gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm3 更新配…...