写点什么

Spring 如何选择类构造器

用户头像
申屠鹏会
关注
发布于: 2020 年 08 月 16 日



抛出问题:



@Component
public class IndexService {
@Autowired
OrderDao orderDao;
public IndexService() {
}
public IndexService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public IndexService(OrderDao orderDao, Luban luban) {
}
}




这样一个类,Spring在实例化的时候会用哪个构造方法,大部分人应该会猜是无参构造方法,可以说是对的,但不完全对

再换一个:



@Component
public class IndexService {
@Autowired
OrderDao orderDao;
public IndexService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public IndexService(OrderDao orderDao, Luban luban) {
}
}




如果是这样呢?

Spring还是会选择无参的,小伙伴可以自行尝试



定位源码



避免有些小伙伴没看过Spring源码,这里从最最开始的源头开始摘录处理

(其实这也是实例化Bean的路径。。)



代表一个方法

org.springframework.context.support.AbstractApplicationContext#refresh

org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

org.springframework.beans.factory.support.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);
/**
* 检测一个类的访问权限spring默认情况下对于非public的类是允许访问的。
*/
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);
}
/**
*
* 如果工厂方法不为空,则通过工厂方法构建 bean 对象
@Bean其实就是通过这种方法来实例化的
*/
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
/**
* 从spring的原始注释可以知道这个是一个Shortcut,什么意思呢?
* 当多次构建同一个 bean 时,可以使用这个Shortcut,
* 也就是说不在需要次推断应该使用哪种方式构造bean
* 比如在多次构建同一个prototype类型的 bean 时,就可以走此处的hortcut
* 这里的 resolved 和 mbd.constructorArgumentsResolved 将会在 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) {
// 通过构造方法自动装配的方式构造 bean 对象
return autowireConstructor(beanName, mbd, null, null);
}
else {
//通过默认的无参构造方法进行
return instantiateBean(beanName, mbd);
}
}
// 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);
}
// No special handling: simply use no-arg constructor.
//使用默认的无参构造方法进行初始化
return instantiateBean(beanName, mbd);
}




我们前面都不看,只看一行determineConstructorsFromBeanPostProcessors这个方法。determineConstructorsFromBeanPostProcessors这个方法就是今天的重头戏,他会返回一个 Constructor<?>[],这个就是Spring会选择的构造器数组,返回一个数组?不要紧,Spring会在autowireConstructor里选择一个来进行初始化,怎么选择这就是第二节的内容



Spring怎么判断类有几个构造器



determineConstructorsFromBeanPostProcessors源码分析



public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {
// Let's check for lookup methods here..
/**
* 处理含有@Lookup注解的方法
* 如果集合中没有BeanName,则走一遍Bean中所有的方法,过滤是否含有Lookup方法
*/
if (!this.lookupMethodsChecked.contains(beanName)) {
try {
/**
* 在方法中循环过滤所有的方法
*/
ReflectionUtils.doWithMethods(beanClass, method -> {
/**
* 获取method上的@Lookup注解
*/
Lookup lookup = method.getAnnotation(Lookup.class);
/**
* 存在此注解的话,就将方法和注解中的内容构建LookupOverride对象,设置进BeanDefinition中
*/
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");
}
}
});
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
}
/**
* 无论对象中是否含有@Lookup方法,过滤完成后都会放到集合中,证明此Bean已经检查完@Lookup注解了
*/
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);
/**
* 用户指定的必须用的构造函数
*/
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;
}
//find加了@Autowired的构造函数
AnnotationAttributes ann = findAutowiredAnnotation(candidate);
/**
* 注解不存在,则再通过方法获取用户类,如果是用户类则返回用户类,还判断了CGLIB情况,
* CGLIB情况则返回目标类,然后获取参数一致的构造函数再获取注解
*/
if (ann == null) {
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) {
if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
/**
* 获取@Autowired注解中required属性的值
* 值得注意的是如果是默认的即没加required
* 这里返回的也会是true
*/
boolean required = determineRequiredStatus(ann);
if (required) {
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;
}
}
/**
* 不为空,则直接用candidates的
*/
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.isWarnEnabled()) {
logger.warn("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的。,那么就选用它
*/
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
/**
* 如果有两个,然后primaryConstructor和defaultConstructor都不为null
* 且两个不相等,则两个都作为返回值
*/
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
/**
* 如果只有primaryConstructor,且primaryConstructor不为null,则使用他
*/
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
/**
* 啥都不满足就是没有
* 即Spring会用他自己的默认的构造函数(其实就是无参构造)
*/
else {
candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}




看注释应该比较清晰了,大致总结一下:



  1. Spring会首先获取你声明的所有构造函数

  2. 然后会循环获取到的所有构造函数,判断有没加@Autowired注解,有加的会放到候选构造函数集合candidates里面去,默认@Autowired为true,check存在多个@Autowired为true的构造函数,有一个@Autowired为true的构造函数的构造函数就会设置为requiredConstructor,如果存在一个参数个数为0的构造函数(也只会有一个,就是无参构造函数),会设置为defaultConstructor

  3. 最后判断选择逻辑如下:



  • 如果candidates不为空,在再加上requiredConstructor为null且defaultConstructor不为null,则选用candidates+defaultConstructor,否则则选用candidates

  • 如果声明的只有一个构造函数,且参数个数大于0的,则直接选用返回

  • 如果有两个声明的构造函数,然后primaryConstructor和defaultConstructor都不为null且两个不相等,则两个都作为返回值

  • 如果只有primaryConstructor,且primaryConstructor不为null,则使用他

  • 啥都不满足就是没有即Spring会用他自己的默认的构造函数(其实就是无参构造)



给几个例子



例子一(只有无参):



@Component
public class IndexService {
@Autowired
OrderDao orderDao;
public IndexService() {
}
}




值得注意的是这种只有无参的,determineConstructorsFromBeanPostProcessors返回null





如果看懂了上面代码就很好理解,Spring为什么这么做呢?因为Spring认为你提供一个无参构造的,相当于没有提供构造函数,Spring会使用自己的无参构造,效果是一致的。



例子二(只有一个有参)



@Component
public class IndexService {
@Autowired
OrderDao orderDao;
public IndexService(OrderDao orderDao) {
}
}




也很好理解,在上面有一个if,如果只有一个有参那么会返回此构造





例子三(结合上面两个,既提供无参又提供有参)





看上面代码,如果提供了两个构造,但是又没加@Autowired,然后又不满足上面第3.3那个if,那么不会选用你提供的这两个,直接返回Null,因为Spring不知道你想选择哪一个



例子四(我们怎么返回多个构造函数让Spring去抉择选用)



其实就是这个方法determineConstructorsFromBeanPostProcessors,怎么返回一个数组

如果能完全看懂上面代码。那么要返回一个[]。就需要如下写了:



@Component
public class IndexService {
@Autowired
OrderDao orderDao;
public IndexService() {
}
@Autowired(required = false)
public IndexService(OrderDao orderDao) {
this.orderDao = orderDao;
}
@Autowired(required = false)
public IndexService(OrderDao orderDao, Luban luban) {
}
}






其实也很好理解就是如果你要让Spring后面去判断哪个构造函数最适合,你得先用@Autowired来声明哪些个来参与判断,这里值得注意的是required 这个属性必须显示声明,否则默认为true,那就只能有一个构造被 @Autowired标注了,这里也只会返回一个,其实也就是上面的requiredConstructor

看完这些例子,就知道抛出问题的第二个肯定会return null了,不信的话大家可以尝试下,如果return null,那么Spring会利用反射调用无参构造进行Bean的初始化

好,有了多个构造,那么Spring怎么选择哪一个构造进行初始化呢,是选参数多的还是参数少的,或者说是public的呢?这个后面再说



发布于: 2020 年 08 月 16 日阅读数: 62
用户头像

申屠鹏会

关注

enjoy~ 2018.11.08 加入

https://xabc.site

评论

发布
暂无评论
Spring如何选择类构造器