Spring最终也是通过类的构造方法来创建bean的,本篇文章详细分析下Spring是如何选用构造方法的。
目录
一、场景分析
二、createBeanInstance(),创建bean实例
三、determineCandidateConstructors()推断构造方法
四、autowireConstructor(),找到构造方法以及对应的参数值
五、针对@Bean的处理
Spring中的一个bean,需要实例化得到一个对象,而实例化就需要用到构造方法, 一个类中的构造方法一般分为两种情况:
1.只有一个构造方法
只有一个无参的构造方法:如果只有一个无参的构造方法,那么实例化就只能使用这个无参的构造方法;
只有一个有参的构造方法:如果只有一个有参的构造方法,那么实例化时能使用这个构造方法吗?要分情况:
① 使用AnnotationConfigApplicationContext,会使用这个构造方法进行实例化,Spring会根据构造方法的参数信息去找bean,然后传给构造方法;
② 使用ClassPathXmlApplicationContext,表示使用XML的方式来注册bean,在XML中指定构造方法的参数值(手动指定),或者配置autowire=constructor让Spring自动去找bean作为构造方法参数值。
2.有多个构造方法
上面是只有一个构造方法的情况,如果有多个构造方法呢?还得看多个构造方法中有没有无参的构造方法,一个类存在多个构造方法,那么Spring进行实例化之前,该如何去确定到底用哪个构造方法呢? 具体分析如下:
2.1. 如果开发者指定了想要使用的构造方法,那就用指定的构造方法,指定构造方法的方式:
① xml中的
② 通过@Autowired注解,@Autowired注解可以写在构造方法上(构造方法注入),所以哪个构造方法上写了 @Autowired注解,表示开发者想使用哪个构造方法,但是,它和第一种方式有所不同,通过 xml的方式,我们直接指定了构造方法的参数值,而通过@Autowired注解的方式,只是告诉Spring我们想要用的那个构造方法,但是需要Spring 通过byType+byName的方式去找到符合条件的bean作为构造方法的参数值;
③ 通过getBean()或者beanDefinition.getConstructorArgumentValues()指定,使用getBean()来指定要使用的构造方法,该bean必须是懒加载的或者原型的才会生效;
2. 如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法,让Spring自动选择构造方法的配置方式:
① 基于ClassPathXmlApplicationContext在xml中配置,通过ClassPathXmlApplicationContext可以在xml中指定某个bean的autowire为constructor(autowire="constructor"),这个属性表示通过构造方法自动注入,所以需要自动的去选择一个构造方法进行自动注入,因为是构造方法,所以顺便进行实例化 (规则:找参数最多的那个构造方法);
② 基于AnnotationConfigApplicationContext配置,通过设置注入模式来解决:
AnnotationConfigApplicationContext context1 = new AnnotationConfigApplicationContext();
context1.register(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
//通过设置注入模式来解决:beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR)
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
context1.registerBeanDefinition("userService",beanDefinition);
context1.refresh();
UserService userService1 = (UserService) context1.getBean("userService");
userService1.test();
3. 如果开发者也没有让Spring自动去选择构造方法,默认情况下,Spring会用无参构造方法,如果没有无参构造方法,则报错;
当然,还有一种情况,就是多个构造方法上写了@Autowired注解,那么此时Spring会报错; 但 是,因为@Autowired还有一个属性required,默认为ture,所以一个类中,只能有一个构造方法标 注了@Autowired或@Autowired(required=true),有多个会报错,但是可以有多个构造方法标注@Autowired(required=false,这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法。
总结
1、默认情况,用无参构造方法,或者只有一个构造方法就用那一个;
2、开发者自己指定了构造方法入参值:通过getBean()或者beanDefinition.getConstructorArgumentValues()指定,那就用所匹配的构造方法;
3、开发者想让Spring自动选择构造方法以及构造方法入参值,autowire="constructor";
4、开发者通过@Autowired注解去指定用哪个构造方法,但是希望Spring自动找构造方法的入参值;
在xml中指定某个bean的autowire为constructor和在构造方法上加@Autowired注解的区别? @Autowired注解是告诉Spring我们要用哪个构造方法,只是需要Spring帮我们去找构造方法的入参值,而在xml中指定某个bean的autowire为constructor,表示Spring自动的帮我们去选择要用哪个构造方法,并且选择出来的这个构造方法如果有参数的话,Spring还得帮我们自动的去找到入参的值。
在Bean的生命周期流程中,在经过类加载以及实例化前的步骤后,就会执行createBeanInstance方法来实例化创建对象,而在createBeanInstance()中就会进行推断构造方法,具体流程如下:
1.根据BeanDefinition加载类得到Class对象;
2. 如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返 回;
3.处理加了@Bean注解的类对应的BeanDefination;
4.判断在此之前有没有缓存推断出来的构造方法(args == null才会去判断),如果有,就用缓存好的构造方法来创建对象,缓存的构造方法分为两种情况:
① autowireNecessary为true,缓存的是有参的构造方法,调用autowireConstructor(),在此方法内会拿到缓存好的构造方法入参并进行实例化;
② autowireNecessary为false,缓存的是无参的构造方法,调用instantiateBean()直接进行实例化;
5.在经过步骤3没有找到构造方法时,Spring就在 determineConstructorsFromBeanPostProcessors()方法中,调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法得到哪些构造方法是可以用的;
6.如果存在可用的构造方法,或者当前BeanDefinition的autowired是 AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean 的时候指定了构造方法参数值(通过getBean()的方式),那么就调用autowireConstructor()方法自动构造一个对象;
7. 最后,如果上述情况都不符合,就根据无参的构造方法实例化一个对象;
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());
}
// BeanDefinition中添加了Supplier,则调用Supplier的get()来得到对象
Supplier> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断
boolean resolved = false;
boolean autowireNecessary = false;
//参数args是通过getBean()传进来的,调用getBean()时,没有指定构造方法的参数,才会判断有没有缓存(对于Spring而言,如果开发者通过getBean()提供了参数就不缓存)
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
//resolvedConstructorOrFactoryMethod 起到缓存的作用,用来缓存构造方法
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
// autowireNecessary表示有没有必要进行注入,如果当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false,否则为true,表示需要给构造方法参数注入值
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
// 如果确定了当前BeanDefinition要用的构造方法,那么看是否需要进行对构造方法参数的依赖注入(构造方法注入)
if (autowireNecessary) {
// 找到的是有参的构造方法,方法内会拿到缓存好的构造方法的入参(走到这一步也就代表当前BeanDefination要用的构造方法以及构造方法的入参值都已经有了)
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 找到的是无参的构造方法,直接进行实例化
return instantiateBean(beanName, mbd);
}
}
// 如果没有找过构造方法,那么就开始找了
// Candidate constructors for autowiring?
// Spring提供的一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法
// 比如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来,具体看代码实现会更复杂一点
Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 如果推断出来了构造方法,则需要给构造方法赋值,也就是给构造方法参数赋值,即构造方法注入
// 如果没有推断出来构造方法,但是autowiremode为AUTOWIRE_CONSTRUCTOR,则也可能需要给构造方法赋值,因为不确定是用无参的还是有参的构造方法
// 如果通过BeanDefinition指定了构造方法参数值,那肯定就是要进行构造方法注入了
// 如果调用getBean的时候传入了构造方法参数值,那肯定就是要进行构造方法注入了
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
//自动地去找当前BeanDefination要用哪个构造方法,以及对应的入参值是什么
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);
}
AutowiredAnnotationBeanPostProcessor实现了determineCandidateConstructors方法,核心就是去找哪个构造方法上面加了@Autowired注解,当然,最终找到的候选的构造方法也不一定全都加了@Autowired注解,具体分析如下:
1. 判断candidateConstructorsCache(缓存)中有没有当前类对应的构造方法,如果有,就可以直接拿来用,如果没有,就会利用反射机制去获取当前类所有的构造方法;
2. 声明相关构造方法变量,如requiredConstructor、defaultConstructor用于记录后续找到的构造方法;
说明:requiredConstructor用来记录加了@Autowire(required = true)的构造方法,一个类中只能有一个required为true的构造方法,defaultConstructor用来记录默认无参的构造方法;
3. 遍历所有构造方法,判断这些构造方法上面有没有加@Autowired注解:
3.1. 没有加@Autowired注解:如果没有加@Autowired注解的构造方法但可以找到一个无参的构造方法,就记录;
3.2. 加了@Autowired注解:只有一个加了@Autowired(required = true)的构造方法或者有多个加@Autowired(required = false)注解的构造方法都会记录下来;
注意,在3.2.中有两种异常的情况:
3.2.1. 如果当前构造方法上加了@Autowired注解,在此之前已经有@Autowired(required = true)的构造方法,则会抛异常;
3.2.2. 如果当前构造方法上加了@Autowired(required = true)注解,在此构造方法之前,已经有一个加了@Autowired(required = false) 的构造方法,则会抛异常;
总结:上述的步骤3循环结束后,candidates中要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的构造方法或者candidates为空,对于存在有参、并且没有添加@Autowired的构造方法,这里没有做处理;
4. 确定候选的构造方法:
4.1. 在步骤3 candidates不为空的前提下,如果不存在required为true的构造方法,则所有required为false的构造方法和无参构造方法都是符合的,如果只存在一个required为true的构造方法,那就只有这一个是符合的;
4.2. 没有添加@Autowired注解的构造方法,但是类中只有一个构造方法,并且是有参的构造方法,可以作为候选的构造方法;
4.3. 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的;
/**
* SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors(),从方法定义的含义来看,是确定候选的构造方法
* 想要达到的效果是去找当前这个类里面有哪些构造方法可以用,而AutowiredAnnotationBeanPostProcessor实现了determineCandidateConstructors方法
* 核心就是去找哪个构造方法上面加了@Autowired注解,当然最终找到的构造方法也可能没有加@Autowired,具体的细节还得看下面方法的实现
*/
@Override
@Nullable
public Constructor>[] determineCandidateConstructors(Class> beanClass, final String beanName)
throws BeanCreationException {
// Let's check for lookup methods here...
if (!this.lookupMethodsChecked.contains(beanName)) {
// 判断beanClass是不是java.开头的类,比如String
if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
try {
Class> targetClass = beanClass;
do {
// 遍历targetClass中的method,查看是否写了@Lookup方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Lookup lookup = method.getAnnotation(Lookup.class);
if (lookup != null) {
Assert.state(this.beanFactory != null, "No BeanFactory available");
// 将当前method封装成LookupOverride并设置到BeanDefinition的methodOverrides中
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> candidates = new ArrayList<>(rawCandidates.length);
// 用来记录required为true的构造方法,一个类中只能有一个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;
}
// 当前遍历的构造方法是否加了@Autowired
MergedAnnotation> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
// 如果beanClass是代理类,则得到被代理的类的类型
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) {
// 整个类中如果有一个required为true的构造方法,那就不能有其他的加了@Autowired的构造方法
//在此构造方法之前,已经有一个加了@Autowired(required = true)的构造方法,则会抛异常
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)的构造方法,则会抛异常
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
// 记录唯一一个required为true的构造方法
requiredConstructor = candidate;
}
// 记录所有加了@Autowired的构造方法,不管required是true还是false
// 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中
candidates.add(candidate);
// 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法
}
else if (candidate.getParameterCount() == 0) {
// 记录唯一一个无参的构造方法
defaultConstructor = candidate;
}
// 有可能存在有参、并且没有添加@Autowired的构造方法,这里没有做处理
}
//上面for循环执行完之后,得到candidates的几种情况:
//1.只有一个加了@Autowired(required = true)的构造方法
//2.只有一个或多个加了@Autowired(required = false)的构造方法
//3.candidates为空
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
// 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的
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));
}
}
// 如果只存在一个required为true的构造方法,那就只有这一个是合格的
candidateConstructors = candidates.toArray(new Constructor>[0]);
}
// 没有添加了@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor>[] {rawCandidates[0]};
}
// primaryConstructor不用管
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
candidateConstructors = new Constructor>[] {primaryConstructor, defaultConstructor};
}
// primaryConstructor不用管
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
candidateConstructors = new Constructor>[] {primaryConstructor};
}
else {
// 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的
candidateConstructors = new Constructor>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
总结:
Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
返回的args有三种情况:① ctors为null ②ctors中有一个构造方法 ③ctors中有多个构造方法
determineCandidateConstructors()方法中推断构造方法的不同情况:
1.如果通过getBean()传入了参数值,那构造方法要用的入参就确定好了,或者在BeanDefinition中缓存了具体的构造方法或构造方法参数值,如果存在,则直接使用该构造方法进行实例化;
2.如果没有确定的构造方法或构造方法参数值,接下来就要找确定的构造方法以及对应的参数值,如果找到了确定的构造方法,接下来也得找到对应的参数值:
2.1. 如果没有确定的构造方法,那么则找出类中所有的构造方法;
2.2. 如果只有一个无参的构造方法,并且没有指定所要使用的构造方法参数值,那么直接使用无 参的构造方法进行实例化;
2.3.如果有多个可用的构造方法或者当前Bean需要自动通过构造方法注入(Spring自动选择构造方 法以及构造方法入参值);
2.4. 根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数,如果我们没有配置构造方法参数的话,这个minNrOfArgs的值就是0;
2.5. 对所有的构造方法进行排序,参数个数多的在前面;
2.6. 遍历每个构造方法 ,如果是调用getBean方法时所指定的构造方法参数值,就直接利用这些值,如果不是调用getBean方法时所指定的构造方法参数值,那么则根据构造方法参数类型找值;
2.7. 如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不 一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值, 这个时 候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的;
/*
* 根据给定的构造器列表(如果给的构造器参数是空的,那么则会去获取该类的构造器数组),Spring去找出一个最适合的构造器,然后通过这个构造器去实例化对象出来.
* beanName:Bean的名称 mbd:该bean的BeanDefinition chosenCtors:该类的构造器数组
* explicitArgs:构造方法参数,这个参数是通过getBean方法传过来的,我们调用getBean()的时候除了传入beanName/beanClass以外,还可以传入其它参数
* 如果传入了其它参数,那么Spring认为这些参数是构造器初始化对象时的构造方法参数列表,而这个其它参数就是此时的explicitArgs.
*/
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor>[] chosenCtors, @Nullable Object[] explicitArgs) {
//BeanWrapperImpl是BeanWrapper的实现类,这个类用来存储Bean的名称、Class、实例对象等
BeanWrapperImpl bw = new BeanWrapperImpl();
//初始化这个BeanWrapper
this.beanFactory.initBeanWrapper(bw);
//constructorToUse是我们最终使用的构造器,因为传进来的构造器是个数组,Spring需要筛选出来最适合的构造器,筛选出来最适合的构造器就会赋值给constructorToUse
Constructor> constructorToUse = null;
// argsHolderToUse用来存储用到的构造器的参数,下面的argsToUse的值也是从这个argsHolderToUse中取出来的
ArgumentsHolder argsHolderToUse = null;
//构造方法中使用到的参数列表实际的值
Object[] argsToUse = null;
/*
*下面的if和else判断:1、判断是否传入构造方法参数值列表,如果传入则赋值 2、没有传入则从缓存中去取
*/
// 如果getBean的时候传入了参数,那么Spring就认为我们希望按照指定的构造参数列表去找构造器并实例化对象,这里如果不为空,实际上需要使用的构造方法参数列表值就已经确定了
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
/*
* 这里干的事情很简单,如果这个bean是原型的,进入过当前方法,那么也肯定找到过合适的构造方法和构造参数值列表,在找到
* 合适的构造方法和构造参数值列表后会加入到缓存里面去,此处如果不是第一次进入的话,那么缓存里面已经有了,不用再次去获取
* 此处做的工作就是从缓存中去获取已经找到过并存进来的构造方法和构造参数值列表
* 这个加入缓存的操作在下面的代码里面才有,此处不理解可以看到下面的代码后再去理解
* 还有注意,只有当我们参数中explicitArgs为空的时候,构造器才会被缓存,同样也是下面解释
*/
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
//判断有没有缓存, resolvedConstructorOrFactoryMethod:缓存构造方法
constructorToUse = (Constructor>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached constructor...
//resolvedConstructorArguments 构造方法参数值
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
//constructorArgumentsResolved为true,resolvedConstructorArguments和preparedConstructorArguments必然有一个缓存了构造函数的参数
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
}
// 如果没有确定要使用的构造方法,或者确定了构造方法但是所要传入的参数值没有确定,因为上面有可能从缓存里面已经拿到了,如果拿到了则不需要进if里面去寻找,直接去调用创建实例化操作了
if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
// 如果没有指定构造方法,那就获取beanClass中的所有构造方法作为候选者
Constructor>[] candidates = chosenCtors;
/*
* 如果传入的构造器列表为空,则通过class对象去拿
* 如果bd中设置了允许访问非public的构造器,那么则获取所有的构造器,否则获取public的构造器,这里isNonPublicAccessAllowed的默认值为true.
* 如果获取构造器的时候出错就要抛异常
*/
if (candidates == null) {
Class> beanClass = mbd.getBeanClass();
try {
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);
}
}
//如果只有一个候选构造方法,并且没有指定所要使用的构造方法参数值(explicitArgs,constructorArgumentValues),并且该构造方法是无参的,那就直接用这个无参构造方法进行实例化了
//比如只有一个加了@Autowired注解的无参构造方法就会符合下面的判断
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;
}
bw.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;
// 确定要选择的构造方法的参数个数的最小值,后续判断候选构造方法的参数个数如果小于minNrOfArgs,则直接pass掉
// 最小参数个数,此值需要用来在循环寻找构造器时使用,如果当前循环到的构造器参数值个数小于这个最小值的话,那么说明就是不合适的,没必要继续下去
int minNrOfArgs;
//如果通过getBean传入了参数,Spring就认为我们希望按照指定的构造参数列表去寻找构造器并实例化对象,这里如果不为空则实际上需要使用的构造方法参数列表值就已经确定了
if (explicitArgs != null) {
// 如果通过getBean()传了构造方法的参数值,那么所用的构造方法的参数个数肯定不能少于minNrOfArgs
minNrOfArgs = explicitArgs.length;
}
else {
/*
* 如果我们没有传入构造器参数值列表,就去解析看有没有配置构造器参数列表,例如如下配置(基于xml):
*
*
*
*
* 这个时候,minNrOfArgs的值就是2
* 如果我们没有配置构造器参数的话,这个minNrOfArgs的值就是0
*
* 通过BeanDefination设置:
* AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
* ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
* constructorArgumentValues.addIndexedArgumentValue(1,new OrderService());
* 通过BeanDefinition传了构造方法参数值,因为有可能是通过下标指定了,比如1位置的值,虽然只指定了1个值,但是构造方法的参数个数至少得是2个
*/
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 对候选构造方法进行排序,public的方法排在最前面,都是public的情况下参数个数越多越靠前
AutowireUtils.sortConstructors(candidates);
//差异变量
int minTypeDiffWeight = Integer.MAX_VALUE;
/*
* 有歧义的构造器:就是参数数量一致的,这种情况下的构造器就被列为有歧义的.
* 正常情况下,如果出现有歧义的构造器,那么就使用第一个,这取决于spring设置的宽松模式.
* 默认为宽松,如此的话就默认使用第一个构造器
* 如果设置为严格,则会报错
* 设置宽松/严格模式标志:beanDefinition.setLenientConstructorResolution
*/
Set> ambiguousConstructors = null;
Deque causes = null;
/*
* 下面就是循环的拿构造器去校验判断选取一个合适的构造器了,在此之前我们总结一下上述代码做的事情.
* 1、定义constructorToUse、argsHolderToUse、argsToUse,这些分别用来存后面实际上需要使用的构造器、构造器参数、值等
* 2、如果getBean调用的时候传入了构造器参数,那么argsToUse的值就被赋值为传入的构造器参数,否则就尝试从缓存里面去拿constructorToUse和argsToUse
* 这个缓存就是当bean是原型的时候实例化时找到的合适的构造器等参数,当然如果是第一次进来,或者bean是单例的,那么此缓存中肯定没有这个bean相关的构造器数据
* 3、如果缓存里面有,则直接实例化bean后放到wrapper中并return,如果不存在则需要再次进行一些操作
* 4、缓存不存在,则定义candidates变量,然后将chosenCtors(是前面传入的构造器列表)赋值过去,如果它为空,那么则需要去通过class去拿构造器,拿的时候会去判断
* BeanDefinition中的isNonPublicAccessAllowed,这个isNonPublicAccessAllowed意思为是否允许访问非public的构造器,如果为true,则去获取所有的构造器,否则只获取public的
* 5、定义resolvedValues,这个是后续循环里面需要使用到的构造器使用的参数列表,定义minNrOfArgs,这个是最小参数个数,首先如果getBean传入了构造器参数
* 那么此值就是传入构造参数的长度,否则就尝试看我们有没有配置使用某个构造器,如果都没有,那么这个值就是0了,这个变量用来后面在循环构造器的时候筛选用的
* 6、然后对所有的构造器进行排序,规则为public>其他权限,参数个数多的>参数个数少的,至于为什么排序这个可能是spring认为参数越多的越科学
* 7、差异变量,这个看循环里面的代码才能理解
* 8、定义ambiguousConstructors为有歧义的构造器,意思就是如果两个构造器参数一致,那Spring就不知道该去用哪个,这时这两个构造器就被放入ambiguousConstructors集合中,他们两个就是有歧义的构造器
* ================================================================================
* 下面循环里面需要搞清楚的就是它具体是如何选取到合适的构造器来使用
*/
for (Constructor> candidate : candidates) {
// 参数个数
int parameterCount = candidate.getParameterCount();
/**
*前面说了[constructorToUse]这个变量是当前确定使用的构造器,如果它不为空,那么说明我们已经确定了使用哪个构造器,那么就没必要继续下去了.
*但[argsToUse.length > paramTypes.length]这个就比较难理解,注意每次循环以后argsToUse的值就会改变为那次循环的构造器的参数
*如果当前拿到的argsToUse参数列表的长度大于当前这个构造器的长度,那么说明上一次拿到的这个argsToUse比当前的这个更合适(上面也说过,Spring认为参数越多的越科学)
*这里可以注意一下前面sort排序的时候,构造参数个数越多的越靠前,所以这里就能用长度判断后直接break,因为如果上一次循环的构造器参数列表为2个
*那么这一次(也就是下一次)的构造参数列表肯定不会比2大,那么说明对于参数个数而言,上一次的参数个数肯定不会比这一次少,那么肯定就更合适了
*/
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;
}
/**
* 如果当前构造器的参数数量比最小参数列表数量小的时候,那么跳过这个构造器.
* minNrOfArgs的值有两个地方赋值了:
* 1、如果我们getBean时传入了其他参数,那么其他参数的个数就是minNrOfArgs的值
* 2、如果我们getBean没有传参数,那么minNrOfArgs的值就是我们配置让Spring指定使用某些参数的构造器,那么我们配置的参数列表数量也就是当前的minNrOfArgs
* 3、如果上述的情况都不存在,那么minNrOfArgs就是0了,大多数时候都是这种情况,如果都没配置,那么就得Spring自己判断而不存在此处的筛选了.
* 所以总结来说此处就是做了一个根据我们自己定义的来筛选的操作
* */
if (parameterCount < minNrOfArgs) {
continue;
}
//存储构造器需要的参数
ArgumentsHolder argsHolder;
Class>[] paramTypes = candidate.getParameterTypes();
/**
* 此处resolvedValues不为空则说明没有通过getBean()指定构造方法参数值,即explicitArgs为空
* 因为上面的代码是如果explicitArgs不为空,则不会对resolvedValues赋值,否则就对resolvedValues赋值
* 此处先看else的代码,会更清晰,如果传入的参数为空,那么则会去拿参数了
*/
if (resolvedValues != null) {
try {
// 如果在构造方法上使用了@ConstructorProperties,那么就直接取定义的值作为构造方法的参数名,如果没有这个注解,那么此处paramNames为空的
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
// 这里为空则代表我们没有通过注解去自定义参数名称,则通过ParameterNameDiscoverer去解析拿到构造器的参数名称列表
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
//解析拿到参数名称列表
paramNames = pnd.getParameterNames(candidate);
}
}
/**
* 根据参数类型、参数名找到对应的bean对象,此处将构造器需要的参数值拿出来后并封装到了argsHolder中去
* 因为有可能在在BeanDefinition中没有指定构造方法参数值或者只指定了一部分
*/
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.
/**
* 没有通过BeanDefinition指定构造方法参数值,但是在调getBean方法是传入了参数值,那就表示只能用对应参数个数的构造方法
* 到了这个else里面来,说明getBean调用的时候传入了构造器参数,那么就说明我们希望按照指定的构造器去初始化Bean.
* 那么这里就需要判断当前构造器的参数个数是否和我们希望的个数一样,如果不是,那么就循环去找下一个构造器,
* 如果和我们希望的是一样的,那么就将我们给的参数封装到argsHolder里面去
*/
if (parameterCount != explicitArgs.length) {
continue;
}
// 不用再去BeanFactory中查找bean对象了,已经有了,同时当前正在遍历的构造方法就是可用的构造方法
argsHolder = new ArgumentsHolder(explicitArgs);
}
/**
* 当到达这里的时候,至此我们拿到了: 1、构造器 2、构造器需要的参数和值
* 那么这里就去计算前面定义的那个差异值(因为有可能存在多个参数个数相同并且都是参数个数最多的构造方法)
* 注意这里的:isLenientConstructorResolution意思是是否为宽松的模式,为true的时候是宽松,false的时候是严格,默认为true,这个东西前面已经说了.
* 这个差异值越小越那就说明越合适.
* 具体差异值如何计算出来的这个可以自行去看里面的代码,argsHolder.getTypeDifferenceWeight(paramTypes)
*/
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
/**
* 值越小越匹配
* 如果本次计算到的差异值比上一次获取到的差异值小,那么就需要做这几件事:
* 1、设置constructorToUse为当前的这个构造器
* 2、设置参数和参数值
* 3、给差异值赋值为当前计算出来的差异值
* 4、清空有歧义的集合(因为此时我们已经得到了更合适的构造器,所以有歧义的构造器里面保存的构造器已经没有存在的意义了)
*/
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
/**
* 值相等的情况下,记录一下匹配值相同的构造方法
* 如果已经找到过一次构造器,并且当前的差异值和上一次的差异值一致的话,那么说明这两个构造器是有歧义的,
* 那么就将这两个构造器放到[有歧义的构造器]集合中去.
*/
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
// 遍历结束 情况1: 选出了那个分数最小的构造方法
// 情况2:找到多个分数相同的构造方法
// 情况3:没有找到
// 如果没有可用的构造方法,就取记录的最后一个异常并抛出
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)");
}
/**
* 如果有可用的构造方法,但是有多个
* 如果存在歧义的构造器集合不为空,并且当前BeanDefinition为严格模式,那么则抛出异常,只有当BeanDefinition为宽松模式时,这种情况才不会抛异常
*/
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);
}
// 如果没有通过getBean方法传入参数,并且找到了构造方法以及要用的入参对象则缓存
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;
}
加了@Bean注解的实例化,也是在createBeanInstance()中进行的,Spring会把@Bean修饰的方法解析成BeanDefinition:
1. 如果方法不是static的,那么解析出来的BeanDefinition中:
① factoryBeanName为AppConfig所对应的beanName,比如"appConfig"
② factoryMethodName为对应的方法名,比如"userService"
③ factoryClass为AppConfig.class
2. 如果方法是static的,那么解析出来的BeanDefinition中:
① factoryBeanName为null
② factoryMethodName为对应的方法名,比如"userService"
③ factoryClass也为AppConfig.class
在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示 factoryMethod是不是唯一的,在一般情况下@Bean生成的BeanDefinition对应的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,例如:
@ComponentScan("com.cgcstudy.service7")
@Configuration
public class AppConfig {
@Bean
public UserService userService(){
return new UserService();、
}
@Bean
public UserService userService(OrderService orderService){
return new UserService();
}
}
虽然有两个@Bean,但是肯定只会生成一个userService的Bean,那么Spring在处理@Bean时,也只会生成一个userService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个 BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来 beanDefinitionMap中已经存在一个userService的BeanDefinition了,那么会把之前的这个 BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了,并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。