前言
在spring创建一个bean的时候,假如我们配置的类里有多个构造方法,spring用的是哪个构造方法?它是怎么选择的?
一般我们在配置bean的时候,基本上都没有关注构造方法,因为每一个类都默认自带一个无参的构造,spring会默认的选择无参构造。但是假如我们配置类里有多个构造方法的话,spring的查找逻辑如下:
- 只有一个构造方法的,不管这个构造方法是什么,以及加没加 @Autowried都选择这个,因为没有其他的选择
- 有多个构造方法的,如果都没有加@Autowried注解。那么默认找无参构造,这种情况下没有无参构造那么程序会抛出异样。
- 有多个构造方法的,如果有加@Autowried注解的。那么会找@Autowired(required = true)的,默认required为true,并且在这个时候如果还有其他的构造方法加了@Autowried注解,不管required为true还是false都会抛出异常。说明@Autowired(required = true)一旦存在就不能存在其他的@Autowired注解的构造(这表示用户指定了这个构造方法创建bean)
- 有多个构造方法的,如果有加@Autowried注解的,没有@Autowired(required = true)的,那么会找到所有的@Autowired(required = false)放入候选集合,方便后续推断,如果此时有默认的无参构造,那么也会把无参构造放入这个集合(表示没有指定,需要spring帮用户)
查找构造
spring的推断构造方法发生在spring bean的生命周期中的实例化bean这一阶段,在要创建bean对象时,需要找到是用那个构造方法来创建对象,具体代码如下:
代码调用链:refresh—>finishBeanFactoryInitialization—>preInstantiateSingletons—>getBean—>doGetBean—>getSingleton—>AbstractBeanFactory.this.createBean—>doCreateBean—>createBeanInstance
/*
* 这里创建bean的实例。spring中三种实例化bean的区分:
* 开发者自己实现的接口创建实例(实现Supplier接口,实现创建对象逻辑)
* 工厂方法创建实例(在配置类中使用@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);
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());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
/*
* 属于工厂方法创建
* */
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
/*
* 属于正常扫描的bean
* */
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (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);
}
}
/*
* 下面开始推断构造函数:三步走
* 1.查找使用了@Autowrite注解的函数,如果有则走该逻辑的构造
* 2.如果没有,则看看是否bean定义中是否指定了构造函数,如果有则使用
* 3.以上都不行,则直接使用默认的无参构造函数
* */
/*
* 如何推断构造方法?:
* 所有的构造函数中是否有且仅有一个@Autowrited注解并且属性required=true的构造函数,如果有那么就是他。如果有多个@Autowrited注解的,那么required只能为false,则抛出异常
* 如果所有的构造函数没有@Autowrited注解,且又bean定义中又没有指定构造函数,那么必须是无参构造函数。否则抛出异常
* 有多个@Autowrited注解的构造函数,但是required=false的会加入到候选组集合返回(如果没有@Autowrited注解默认的无参构造也会加入集合),进一步推断
*
*
*
* */
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);
}
上述代码,determineConstructorsFromBeanPostProcessors这个方法是找到候选构造方法的集合,如果存在候选那么就拿着这些候选的构造方法进一步推断。那么他是如果找候选构造的呢?
determineConstructorsFromBeanPostProcessors方法
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
//这里查找构造方法的实现类是:AutowiredAnnotationBeanPostProcessor
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
return null;
}
/*
* 推断候选的构造方法:
* 1.检查所有的构造方法是否存在@Autowrited注解。
* 2.如果有@Autowrited注解的构造且required=true的,那么表示这是用户指定的构造方法,如果还有其他的方法加了@Autowrited注解的,直接抛出异常
* 3.如果没有@Autowrited注解且required=true的,那么检查是否存在@Autowrited注解required=false的,存在的话,如果还有默认的构造,有这两类则加入到候选组
* 4.符合上述条件的都会加入到候选组,没有则返回null
* */
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {
/*
* 检查@Lookup注解。
* 单例bean依赖原型bean的是需要将原型bean转化为单例
* */
// Let's check for lookup methods here...
if (!this.lookupMethodsChecked.contains(beanName)) {
if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
try {
Class<?> targetClass = beanClass;
do {
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Lookup lookup = method.getAnnotation(Lookup.class);
if (lookup != null) {
Assert.state(this.beanFactory != null, "No BeanFactory available");
LookupOverride override = new LookupOverride(method, lookup.value());
try {
RootBeanDefinition mbd = (RootBeanDefinition)
this.beanFactory.getMergedBeanDefinition(beanName);
mbd.getMethodOverrides().addOverride(override);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(beanName,
"Cannot apply @Lookup to beans without corresponding bean definition");
}
}
});
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
}
}
this.lookupMethodsChecked.add(beanName);
}
/*
* 先检查候选缓存组,如果有,则表示之前已经推断过了,此时无需再做多余的动作,直接返回。
* */
// 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 {
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);
//存放用户指定的构造方法:即@Autowrited注解为required=true的
Constructor<?> requiredConstructor = null;
//存放无参的默认构造
Constructor<?> defaultConstructor = null;
//获取Kotlin类,我们用不到,忽略
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
//表示所有的构造方法中,不是合成的构造方法有多少个(内部类时,可能会出现合成的构造,正常情况没有)
int nonSyntheticConstructors = 0;
//遍历所有的构造开始推断
for (Constructor<?> candidate : rawCandidates) {
if (!candidate.isSynthetic()) {
//记录正常的构造方法的数量
nonSyntheticConstructors++;
}
//为空不管
else if (primaryConstructor != null) {
continue;
}
/*
* 查找构造方法中是否添加了@Autowrited注解
* */
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
//如果没有加,判断是不是cglib代理的类,如果是,那么找到开发者定义的类,根据参数去匹配真实类的构造并检查是否存在@Autowrited注解
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...
}
}
}
//如果找到了@Autowrited注解
if (ann != null) {
/*
* requiredConstructor != null 说明之前的遍历中已经找到了用户指定的构造函数(@Autowrited:required=true的),
* 再次进入这个分支,说明开发者还配置了其他的构造函数@Autowrited注解,spring不允许这样配置,抛出异常
* */
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) {
/*
* required为true且!candidates.isEmpty() ,同上,抛出异常
* */
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
requiredConstructor = candidate;
}
//符合候选的构造函数,加入集合
candidates.add(candidate);
}
//记录默认的无参构造
else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
/*
* 找到一些候选的构造,且开发者没有指定那一个构造,那么也将无参构造加入候选组
* */
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null) {
if (defaultConstructor != null) {
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));
}
}
//封装为数组
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
//只有一个构造函数的,且不是无参构造
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
//Kotlin相关,不考虑
else 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);
}
这里的查找过程如同最简介的结论一样,看似繁琐,实则所有的过程都是为了找出符合上述结论的结果。这里执行完毕之后,就找到了候选的构造,可这里可能存在多个候选的构造,那么spring要使用哪一个?那么回到createBeanInstance方法中,在这会继续执行autowireConstructor方法找到最符合条件的那个
推断构造 autowireConstructor方法
这个方法的目的是在于在多个候选的构造方法当中找到最符合条件的那个,那么谁才是最符合条件的那个构造方法?先给出结论:
- 先看访问修饰符,如果是public的构造就是最大的,有多个public的,那么谁的参数多就是谁
- 如果参数一样多,那么就要计算参数的差异值,差异值越小越符合,一般来说,Java的基础类型int,long这些是最小的(包括string),其次就是我们配置的引用类型
- 如果参数一样多,且差异值也一样,那么看当前spring环境是宽松还是严格模式(默认宽松),如果是宽松模式,那么就找第一个,严格模式直接抛出异常。
- 如果没有public的,那么这一类分为一组,按照上述逻辑依次计算。
- 如果有参数找不到值的,直接放弃。
代码逻辑如下:
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
//创建构造函数处理器,推断构造函数
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
//bean实例化的包装器
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) {
// Take specified constructors, if any.
Constructor<?>[] candidates = chosenCtors;
//如果没有候选的构造函数,那么直接找默认的无参构造
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
//是否允许非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;
}
//实例化bean
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
/*
* 没有默认的无参构造,那么需要继续推断构造函数
*
* autowiring为true表示自动注入,有@Autowried注解的构造,或者注入模型为AUTOWIRE_CONSTRUCTOR的就是自动注入
*
* minNrOfArgs记录需要的最少的参数值。有可能有多个候选构造,如果构造函数的参数小于这个值,那么肯定就不会是这个构造函数,直接跳过
* 如果开发者指定了参数,那么就是参数的个数就是该值,没有指定则为0
* */
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 对候选构造方法进行排序,public的方法排在最前面,都是public的情况下参数个数越多越靠前
AutowireUtils.sortConstructors(candidates);
/*
* 记录构造函数与参数的最小差异值:
* spring会排序遍历所有的构造函数,并且会去找构造函数上的参数,在寻找的过程中会记录每一个构造函数与参数的匹配程度,越匹配的这个差异值就越小
* */
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) {
// 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();
/*
* 如果进入到了这里,说明开发者没有指定构造参数。
*
* 那么spring会根据每一个参数类型和名称到容器中取匹配合适的值
* */
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 = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
//有些参数的值可能并不在spring当中这是创建参数就会异常,那么就放弃这个构造
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);
}
/*
* 下面这段代码是计算构造函数与参数的差异化的值:
* 根据不同的模式(宽松/严格)计算最小差异值
*
* 宽松模式下:Java的基础类型int,long,double等等以及string的类型权重都是为0,即最小,引用类型一般为2
* */
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
//此次计算的差异值比之前的更小,那么就取当前这个,然后重新赋值给minTypeDiffWeight,以便后续遍历使用
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;//这里复制为null,因为找到合适的了,之前即使有歧义的构造函数已经无所谓了
}
//计算出来差异值一样的多个函数表示存在歧义,加入到歧义队列。constructorToUse != null表示至少已经存在一个构造函数,因为两个及以上才会存在歧义
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()) {
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);
}
//如果开发者没有指定参数,且spring找到了参数,那么将参数缓存。(不会缓存开发者指定的参数,因为不知道何时开发者就会换掉指定的参数)
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
//实例化.走到这里说明已经找到了构造方法
Assert.state(argsToUse != null, "Unresolved constructor arguments");
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
结论
从上述整体流程可以看到,spring在推断构造方法的时候,实际上就是两个方法,一个用来查找候选的构造方法集合,一个就是从集合中找到最符合条件的那个构造方法,找到之后就直接创建对象。