1.前言
在之前的博文《Spring之BeanDefinition》讲述了BeanDefinition的用法,以及如何定制化属性,使bean按照我们期望的方式实例化。
通过总结,实例化方式大概分为三种:
- supplier
- factoryBeanName + factoryMethodName (beanClass + factoryMethodName)
- 构造器
今天我们主要讲解Spring是如何推断出一个唯一的构造器方法,来实例化对象
2.源码分析
相关源码在AbstractAutowireCapableBeanFactory#createBeanInstance
分成三个优先级
- 第一优先级通过supplier实例化bean对象
- 第二优先级通过factoryMethod实例化bean对象
- 第三优先级才是我们今天讨论的主角,通过推断构造方法实例化bean对象
2.1 determineConstructorsFromBeanPostProcessors方法解析
这个方法主要循环执行BeanPostProcessor的determineCandidateConstructors方法,我们需要关注其子类实现AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
相关源码注释
删除了处理@Lookup注解的相关源码,有兴趣的可以看我之前的博文 《Spring之@Lookup》
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {
// 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);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
// 查看是不是Kotlin相关的类 查找primaryConstructor
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
// 记录有多少个非合成的构造方法,主要针对两种情况
// case1:只有1个非合成的构造方法(primaryConstructor)
// case2:存在2个非合成的构造方法(primaryConstructor + defaultConstructor)
int nonSyntheticConstructors = 0;
for (Constructor<?> candidate : rawCandidates) {
// 判断是不是非合成的构造方法,不是合成的构造方法参数nonSyntheticConstructors自增1
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
// Kotlin是不是存在合成构造方法的概念?
// 如果存在,针对上面的case1已经可以找到候选的构造方法了,所以不倾向使用这些合成的构造方法
else if (primaryConstructor != null) {
continue;
}
// 查看构造器是否含有@Autowired注解
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
// 如果构造器没有@Autowired注解,则查看类名是否包含$$字符
// 如果存在此特殊字符,表明此类是cglib动态代理产生的子类
// 这时候需要从父类的构造器查看是否含有@Autowired注解
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...
}
}
}
// 能进入此条件表明查询到包含@Autowired注解的构造器
if (ann != null) {
// 1.如果存在一个@Autowired(required = true)注解标注的构造方法
// 则不能再出现@Autowired注解标注的构造方法(无论required等于true或false都报错)
// 2.可以有多个@Autowired(required = false)注解标注的构造方法
// 表明已经解析了一个@Autowired(required = true)注解标注的构造方法
// 不能再出现@Autowired注解标注的构造方法
// TODO 1
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(required = false)注解标注的构造方法
// PS:如果同时存在@Autowired(required = true)注解标注的构造方法和@Autowired(required = false)注解标注的构造方法
// 如果先解析@Autowired(required = true)注解标注的构造方法,则在TODO 1处抛出异常
// 如果先解析@Autowired(required = false)注解标注的构造方法,则在TODO 2处抛出异常
// TODO 2
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
// 表明是第一次解析@Autowired(required = true)注解标注的构造方法
requiredConstructor = candidate;
}
// 这个候选者可以是第一次解析@Autowired(required = true)注解标注的构造方法
// 或者前面解析了n个@Autowired(required = false)注解标注的构造方法,这次解析的同样是
// @Autowired(required = false)注解标注的构造方法
candidates.add(candidate);
}
// 如果没有@Autowired注解标注的构造方法,则将无参构造方法设为defaultConstructor
else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
// 第一优先级:存在候选构造方法
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null) {
// 解析了n个@Autowired(required = false)注解标注的构造方法
// 并且将默认构造方法也当成候选构造方法返回
if (defaultConstructor != null) {
candidates.add(defaultConstructor);
}
// 这里可能存在两种情况:
// 第一种情况:只有一个无参构造方法,并且标记了@Autowired(required = false)注解
// 第二种情况:只有一个有参构造方法,并且标记了@Autowired(required = false)注解
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]);
}
// 第二优先级:只有一个构造方法 且参数大于0
// PS:不存在@Autowired注解标注的构造方法,不存在无参构造方法
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[]{rawCandidates[0]};
}
// 第三优先级:如果有两个非合成的构造方法,且一个对应primaryConstructor 一个对应defaultConstructor
// 主要针对Kotlin相关的类
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
candidateConstructors = new Constructor<?>[]{primaryConstructor, defaultConstructor};
}
// 第四优先级:有且只有一个非合成的构造方法,且对应primaryConstructor
// 主要针对Kotlin相关的类
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);
}
determineConstructorsFromBeanPostProcessors方法总结
1.可以推断出构造方法的情况(不考虑Kotlin相关的类)
- 有且仅有一个@Autowired(required = true)注解标注的构造方法,不能出现其他有@Autowired注解标注的构造方法
- 有多个@Autowired(required = false)注解标注的构造方法,没有@Autowired(required = true)注解标注的构造方法(如果存在defaultConstructor,则将defaultConstructor也加入候选构造方法)
- 不存在@Autowired注解标注的构造方法,有唯一一个有参构造方法
2.注意点
- 如果存在@Autowired注解标注的构造方法,要么推断出构造方法,要么抛出异常
3.个人理解
我们可以把@Autowired(required = true)注解标注的构造方法当成一个写锁,把@Autowired(required = false)注解标注的构造方法当成一个读锁。在并发情况下,只能一个写锁或多个读锁加锁成功 (即推断出了候选构造方法)
举例演示上述各种情况
代码准备
创建实体类
package com.test.ctor.component;
import org.springframework.stereotype.Component;
@Component
public class Elephant{
}
package com.test.ctor.component;
import org.springframework.stereotype.Component;
@Component
public class Monkey {
}
创建配置类
package com.test.ctor.config;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("com.test.ctor")
public class AppConfig {
}
创建启动类
package com.test.ctor;
import com.test.ctor.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
case1 : 有且仅有一个@Autowired(required = true)注解标注的构造方法
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo1 {
private Elephant elephant;
@Autowired(required = true)
public Zoo1(Elephant elephant) {
this.elephant = elephant;
}
}
debug源码,查看推断结果
case2 : 存在多个@Autowired(required = false)注解标注的构造方法 + 无参构造方法
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo2 {
private Elephant elephant;
private Monkey monkey;
public Zoo2() {
}
@Autowired(required = false)
public Zoo2(Elephant elephant) {
this.elephant = elephant;
}
@Autowired(required = false)
public Zoo2(Monkey monkey) {
this.monkey = monkey;
}
}
debug源码,查看推断结果
推断出了@Autowired(required = false)注解标注的构造方法,和无参构造方法
case3 : 不存在@Autowired注解标注的构造方法,有唯一一个有参构造方法
package com.test.ctor.component;
import org.springframework.stereotype.Component;
@Component
public class Zoo3 {
private Elephant elephant;
public Zoo3(Elephant elephant) {
this.elephant = elephant;
}
}
debug源码,查看推断结果
case4 : 同时存在@Autowired(required = true)注解标注的构造方法和@Autowired(required = false)注解标注的构造方法
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo4 {
private Elephant elephant;
private Monkey monkey;
@Autowired(required = true)
public Zoo4(Elephant elephant) {
this.elephant = elephant;
}
@Autowired(required = false)
public Zoo4(Monkey monkey) {
this.monkey = monkey;
}
}
查看结果
直接抛出在上面源码注释中,TODO 1处的异常
2.2 autowireConstructor
进入autowireConstructor方法最少满足下列条件之一
- ctors != null
- mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
- mbd.hasConstructorArgumentValues()
- !ObjectUtils.isEmpty(args)
先简单阐述一下如何满足这四个条件,后面会举例演示其他三种条件用法
- determineConstructorsFromBeanPostProcessors方法推断出候选构造方法
- 自定义BeanFactoryPostProcessor修改了BeanDefinition的autowireMode属性
- 自定义BeanFactoryPostProcessor给BeanDefinition的constructorArgumentValues属性赋值
- 通过beanFactory获取一个懒加载的bean(或者原型bean)的时候,添加参数
分阶段解析
阶段一 处理constructorToUse和argsToUse
- case1 如果方法传入了指定参数,则将这指定参数作为argsToUse
- case2 比如针对原型bean,第一次获取bean的时候,缓存了constructorToUse和argsToUse,第二次则取缓存的constructorToUse和argsToUse。如果出现case1情况,即使缓存了constructorToUse和argsToUse也不会生效
阶段二 获取待处理构造方法
如果determineCandidateConstructors方法推断出候选构造方法,则将这些候选构造方法作为待处理构造方法,否则根据mbd参数判断是将所有构造方法作为待处理构造方法还是将public修饰的构造方法作为待处理构造方法
如果只有一个待处理构造方法,没有特殊条件,且是无参构造方法则直接实例化bean对象
阶段三 确定构造方法最小参数个数
在上文中阐述了进入autowireConstructor方法的四个条件
- ctors != null
- mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
- mbd.hasConstructorArgumentValues()
- !ObjectUtils.isEmpty(args)
条件1、2、3最小参数个数:beanDefinition的constructorArgumentValues属性的属性的元素之和(indexedArgumentValues + genericArgumentValues)
条件4最小参数个数 : 传入参数数组(explicitArgs)的长度
条件1、2与条件3的不同点 :
- 条件3只能从属性constructorArgumentValues中找相匹配的ConstructorArgumentValues.ValueHolder,如果找不到则抛出异常
- 条件1、2 先从属性constructorArgumentValues中找相匹配的ConstructorArgumentValues.ValueHolder(一般不存在,所以条件1、2的最小参数个数一般为0),然后再从beanFactory中查找相关依赖(如果找不到相关依赖也会报错)。
阶段四 给待处理的构造方法排序
虽然只有一行代码,但是这个排序很重要,它最终决定了,以什么构造方法实例化bean对象,如果优先级靠后,就很难成为最后被选中的构造方法。
排序规则
- public > 其他修饰符修饰的构造方法
- 构造方法参数个数从大到小
- code的书写顺序(隐藏规则)
举例演示上述几种情况
demo1
创建实体类Zoo5
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo5 {
private Elephant elephant;
private Monkey monkey;
@Autowired(required = false)
public Zoo5(Monkey monkey) {
this.monkey = monkey;
}
@Autowired(required = false)
Zoo5(Elephant elephant, Monkey monkey) {
this.elephant = elephant;
this.monkey = monkey;
}
}
运行main方法,查看运行结果
使用的是第一个public描述的构造方法(利用第一优先级规则)
demo2
创建实体类Zoo6
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo6 {
private Elephant elephant;
private Monkey monkey;
@Autowired(required = false)
public Zoo6(Elephant elephant) {
this.elephant = elephant;
}
@Autowired(required = false)
public Zoo6(Monkey monkey, Elephant elephant) {
this.monkey = monkey;
this.elephant = elephant;
}
}
运行main方法,查看运行结果
同修饰符,使用的参数个数多的(利用第二优先级规则)
demo3
创建实体类Zoo7
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo7 {
private Elephant elephant;
private Monkey monkey;
@Autowired(required = false)
public Zoo7(Monkey monkey) {
this.monkey = monkey;
}
@Autowired(required = false)
public Zoo7(Elephant elephant) {
this.elephant = elephant;
}
}
运行main方法,查看运行结果
使用的是code书写顺序的第一个
调换两个构造方法位置
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo7 {
private Elephant elephant;
private Monkey monkey;
@Autowired(required = false)
public Zoo7(Elephant elephant) {
this.elephant = elephant;
}
@Autowired(required = false)
public Zoo7(Monkey monkey) {
this.monkey = monkey;
}
}
再次运行main方法,查看运行结果
同修饰符同参数个数,按住code书写顺序选择第一个(利用第三优先级规则)
阶段五 直接跳出循环或者过滤一些不符合要求构造方法
结合阶段四的分析,说明一下跳出循环的条件
// 通过上一阶段的分析,我们知道了构造器优先级顺序,我们来思考一下什么情况下符合下面的条件
// case1:同优先级,已经成功解析了一个参数更多的构造方法
// case2:不同优先级,已经成功解析的优先级更高的构造方法,拥有的参数个数更多
// PS:Spring可能觉得参数更多的构造方法更有价值,就不再处理那些优先级更低,参数个数更少的构造方法了
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;
}
阶段六 一些解析工作
主要将ConstructorArgumentValues.ValueHolder对象转换成ArgumentsHolder对象,有兴趣的小伙伴可以看一下细节
阶段七 计算类型差异权重
这个阶段也很重要,我们在阶段四讨论构造方法优先级的时候说过,如果优先级较低,最后基本很难成为constructorToUse,这个阶段也是唯一一个优先级较低的情况下可能成为constructorToUse的情况。
我们主要说明一下getTypeDifferenceWeight方法
- 如果构造方法的参数是一个接口,最后Spring找到一个接口的实现类注入进去,权重+1
- 如果构造方法的参数是一个基类,最后Spring找到一个基类的子类注入进去,权重+2
- 如果构造方法的参数是一个基类,最后Spring找到一个基类的孙子类注入进去,权重+4 (以此类推)
我们具体说明第一种情况,其他情况类似,就不重复举例
创建接口Bear 实现类WhiteBear 以及实体类Zoo8
package com.test.ctor.component;
public interface Bear {
}
package com.test.ctor.component;
import org.springframework.stereotype.Component;
@Component
public class WhiteBear implements Bear {
}
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo8 {
private Bear bear;
private Monkey monkey;
@Autowired(required = false)
public Zoo8(Bear bear) {
this.bear = bear;
}
@Autowired(required = false)
private Zoo8(Monkey monkey) {
this.monkey = monkey;
}
}
运行main方法,查看运行结果
根据第一优先级规则,应该推断出第一个public修饰的构造方法。但是因为构造方法的参数类型和实际注入进去的类型存在差异,权重值高于另外一个构造方法,所以最后Spring选择了第二个构造方法
如果权重值,优先级和参数个数都一致,根据第三优先级规则,则选择code书写顺序在前面的那个,另外一个放入ambiguousConstructors。如果BeanDefinition的属性值lenientConstructorResolution为false则抛出异常
3.举例演示另外几种条件推断构造方法
mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
创建实体类Zoo9
package com.test.ctor.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Zoo9 {
private Elephant elephant;
private Monkey monkey;
public Zoo9(Elephant elephant) {
this.elephant = elephant;
}
public Zoo9(Monkey monkey, Elephant elephant) {
this.monkey = monkey;
this.elephant = elephant;
}
}
创建bfpp
package com.test.ctor.bfpp;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)registry.getBeanDefinition("zoo9");
beanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
运行main方法,查看运行结果
优先选取参数个数多的(第一优先级一致,根据第二优先级)
mbd.hasConstructorArgumentValues()
创建实体类Swan Zoo10
package com.test.ctor.component;
public class Swan {
private String sign;
public Swan() {
}
public Swan(String sign) {
this.sign = sign;
}
}
package com.test.ctor.component;
import org.springframework.stereotype.Component;
@Component
public class Zoo10 {
private Elephant elephant;
private Swan swan;
public Zoo10(Elephant elephant) {
this.elephant = elephant;
}
public Zoo10(Swan swan) {
this.swan = swan;
}
}
创建bfpp
package com.test.ctor.bfpp;
import com.test.ctor.component.Swan;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class SecondBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) registry.getBeanDefinition("zoo10");
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, new Swan("666"));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
运行main方法,查看运行结果
最终根据我们设置的值,推断构造方法
!ObjectUtils.isEmpty(args)
创建实例类Zoo11
package com.test.ctor.component;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy
public class Zoo11 {
private Elephant elephant;
private Swan swan;
public Zoo11(Elephant elephant) {
this.elephant = elephant;
}
public Zoo11(Swan swan) {
this.swan = swan;
}
}
运行main方法,查看运行结果
跟上一种类似,都是根据我们的传入值推断的构造方法(注意Zoo11为懒加载的,Spring启动就创建的话args为null)
总结
1.如果存在@Autowired注解标注的构造方法,要么推断出构造方法,要么抛出异常
2.如果满足条件进入autowireConstructor方法,则必须推断出唯一构造方法来实例化bean对象
3.注意三大优先级规则
4.理解类型差异权重的计算