Spring 推断构造方法原理
Spring在创建Bean的生命周期中,在实例化得到一个Bean对象的过程中就需要用到Bean的构造方法,一个类(Class)可能存在一个构造方法和多个构造方法的情况,下面对着两种情况来介绍Spring在实例化Bean过程中采用哪个构造方法进行Bean对象的实例化。
一个构造方法
一个类(Class)只有一个构造方法的情况,这个构造方法要么是无参构造方法,要么是有参构造方法,对于一个构造方法的情况,Spring采用构造方法实例化Bean对象的流程:
如果只有一个无参构造方法,那么Spring只能采用这个构造方法进行Bean对象的实例化。
如果只有一个有参构造方法,如果采用AnnotationConfigApplicationContext创建Spring容器时,Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法、如果采用ClassPathXMLApplicationContext创建Spring容器时,表示使用XML的方式来使用bean,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动取选择bean做为构造方法的参数。
多个构造方法
一个类(Class)中有多个构造方法的情况,Spring采用构造方法实例化Bean对象的流程:
如果开发者指定了想要使用的构造方法,那么Spirng就采用这个构造方法。
如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法。
如果开发者没有让Spring自动去选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错。
指定构造方法
开发者指定使用构造方法的方式:
xml中可以使用标签来指定构造方法参数,根据指定构造方法参数的个数,从而确定指定使用的构造方法。
通过@Autowired注解,@Autowired注解可以写在构造方法上,在构造方法上写@autowired注解,表示开发者指定采用这个构造方法,Spring通过byType+byName的方式去查找符合的bean做为构造方法的参数值。
如果在多个构造方法上写了@Autowired注解,那么Spring启动会报错,@Autowired注解有一个属性required,默认值为true,所以一个类中只能有一个构造方法标注@Autowired或@Autowired(required=true),有多个会报错。但是可以有多个@Autowired(required=false),这种情况需要Spring从这些构造方法中取自动选择一个构造方法。
Spring自动选择构造方法
如果开发者没有指定使用的构造方法,则看开发者有没有让Spring自动去选择构造方法,对于Sping自动选择构造方法只能用在ClassPathXMLApplicationContext,AnnotationConfigApplicationContext没有办法去指定bean可以让Spring自动取选择构造方法。可以在xml中指定某个bean的autowire=constructor。
Spring 推断构造方法实现
整体流程
![](https://img-blog.csdnimg.cn/img_convert/8a93a450bfc6342075cd6de034eeb372.png)
AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法会去创建一个Bean实例
根据BeanDefinition加载类得到Class对象
如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返回
如果BeanDefinition中存在factoryMethodName,那么就调用该工厂方法得到一个bean对象并返回
如果BeanDefinition已经自动构造过了,那就调用autowireConstructor()自动构造一个对象
调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法得到哪些构造方法是可以用的
如果存在可用得构造方法,或者当前BeanDefinition的autowired是AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean的时候指定了构造方法参数值,那么就调用autowireConstructor()方法自动构造一个对象
最后,如果不是上述情况,就根据无参的构造方法实例化一个对象
Spring底层实现源码
Spring推断构造方法实例化一个Bean对象底层实现源码位于AbstractAutowireCapableBeanFactory#createBeanInstance方法中。
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来得到对象
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;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
// autowireNecessary表示有没有必要要进行注入,比如当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false,否则为true,表示需要给构造方法参数注入值
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
// 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法进行参数的依赖注入(构造方法注入)
if (autowireNecessary) {
// 方法内会拿到缓存好的构造方法的入参
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 构造方法已经找到了,但是没有参数,那就表示是无参,直接进行实例化
return instantiateBean(beanName, mbd);
}
}
// 如果没有找过构造方法,那么就开始找了
// Candidate constructors for autowiring?
// 提供一个扩展点,可以利用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)) {
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);
}