Spring源码(九)-确定构造方法-determineConstructorsFromBeanPostProcessors
日积月累,水滴石穿 😄
前言
本篇是createBeanInstance
方法的最后一篇文章。第一篇分析了 obtainFromSupplier
、instantiateUsingFactoryMethod
方法。第二篇分析了autowireConstructor
、instantiateBean
方法。前两篇已经将 Spring
如何实例化 Bean 的方式都分析完了。
源码
这篇就分析一下 determineConstructorsFromBeanPostProcessors
方法。该方法的作用是:确定构造方法,在类中寻找被@Autowired
标注的构造方法。
平常我们可以这么使用,告诉 Spring
我要使用这个构造方法实例化对象。
@Autowired
public TestController(ABServiceImpl str){
System.out.println("一个");
}
那 Spring
是怎么去寻找我们所指定的构造方法呢!GO ~ ~ ~。直接打断点飙到 AbstractAutowireCapableBeanFactory
类的createBeanInstance
方法。
// 上面代码省略
// 代码执行到这,说明是第一次创建该bean
// 根据SmartInstantiationAwareBeanPostProcessor确定构造函数,
// 有三个子类,除了AutowiredAnnotationBeanPostProcessor有实现逻辑,其余两个是空实现
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 通过BeanPostProcessor找出了构造方法
// 或者BeanDefinition的autowire属性为AUTOWIRE_CONSTRUCTOR xml中使用了 autowire="constructor"
// 或者BeanDefinition中指定了构造方法参数值 使用了 <constructor-arg>标签
// 或者在getBean()时指定了args
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 就进行有参构造方法实例化Bean
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
// 没啥用
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// 用无参的构造方法来实例化bean
return instantiateBean(beanName, mbd);
上述逻辑挺简单的,需要创建的 Bean 如果是第一次创建,就会走上述逻辑。首先确定构造方法。然后再根据四个条件来判断是使用有参构造方法实例化Bean,还是使用无参的构造方法来实例化Bean。
现在,我们来看本文的重点,调试进入 determineConstructorsFromBeanPostProcessors
方法,该方法位于AbstractAutowireCapableBeanFactory
类。
该方法有两个入参,分别为:
- beanClass:当前创建Bean的Class对象
- beanName:Bean名称
@Nullable
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
//调用determineCandidateConstructors方法
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}
首先有个判断条件,满足判断条件才走逻辑,否则返回 null。hasInstantiationAwareBeanPostProcessors()
方法也就是看 hasInstantiationAwareBeanPostProcessors
属性是否为 true。
protected boolean hasInstantiationAwareBeanPostProcessors() {
return this.hasInstantiationAwareBeanPostProcessors;
}
hasInstantiationAwareBeanPostProcessors
属性什么时候为 true 呢?当添加的 BeanPostProcessor
为 InstantiationAwareBeanPostProcessor
就为 true。addBeanPostProcessor
具体在什么时候被调用后续的博文会讲到。
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
this.beanPostProcessors.remove(beanPostProcessor);
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
// 是否有InstantiationAwareBeanPostProcessor
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
// 是否有DestructionAwareBeanPostProcessor
this.hasDestructionAwareBeanPostProcessors = true;
}
// 添加到 beanPostProcessors 中,到时候会按添加的顺序执行
this.beanPostProcessors.add(beanPostProcessor);
}
通过了判断逻辑,再获得所有BeanPostProcessors
,如果拿到的 BeanPostProcessor
是SmartInstantiationAwareBeanPostProcessor
,就执行 determineCandidateConstructors()
方法。跳转到SmartInstantiationAwareBeanPostProcessor
接口。
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}
SmartInstantiationAwareBeanPostProcessor
接口默认没有对该方法进行实现。具体实现过程交给实现类实现。SmartInstantiationAwareBeanPostProcessor
接口下有三个实现类,分别是 AbstractAutoProxyCreator
、InstantiationAwareBeanPostProcessorAdapter
、AutowiredAnnotationBeanPostProcessor
。前两个类对determineCandidateConstructors
方法是空实现,只有在 AutowiredAnnotationBeanPostProcessor
实现类中才有具体的实现过程。
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {
// Let's check for lookup methods here... 检查查找方法 lookup-methods
// @Lookup就是在这边通过反射发现的
// (xml形式的replace-method和lookup-method在BeanDefinitionParserDelegate中进行解析)
// 如果当前 beanName 在 lookupMethodsChecked 中存在,就不需要再次解析了
if (!this.lookupMethodsChecked.contains(beanName)) {
if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
// 类中是否包含@Lookup注解
try {
Class<?> targetClass = beanClass;
do {
// 遍历当前beanClass中所有的方法
// 并且把这些方法的信息封装为LookupOverride对象加载到mbd中
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 获得方法上的 Lookup 注解
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);
// 将 LookupOverride对象 放入 overrides Set 集合中
mbd.getMethodOverrides().addOverride(override);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(beanName,
"Cannot apply @Lookup to beans without corresponding bean definition");
}
}
//if结束
});
// 获得当前类的父类
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
}
}
// lookupMethodsChecked是一个set,用来记录哪些bean的@Lookup注解被解析了,
//下一次就不用解析了
this.lookupMethodsChecked.add(beanName);
}
// 上述 if 结束 ===
// 先检查 candidateConstructorsCache 中是否缓存了当前bean中可用的构造方法
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 {
// 拿到当前类所有的构造方法(包括public的和非public的),
//如果类中没有写任何构造方法,这里会返回一个无参的构造方法
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);
}
// candidates:用来存储所有被筛选出来的构造方法,其实可以认为,
// 就是把所有@Autowired注解标注的方法放到里面, 但是还多放了一个默认构造方法
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// requiredConstructor:表示被@Autowired标注并且required为true的构造方法
// 因为只允许出现一个这样的构造方法, 所以当这个变量存在值后
// 又出现了一个相同情况的构造方法的话, Spring就会抛出异常
Constructor<?> requiredConstructor = null;
// defaultConstructor:用来保存默认构造方法
Constructor<?> defaultConstructor = null;
// 如果是kotlin的类才起效,如果是java中的类则直接返回null
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;
// 遍历所有的构造方法
for (Constructor<?> candidate : rawCandidates) {
// nonSyntheticConstructors这个变量是和primaryConstructor != null一起使用的,所以也不用管
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;
}
// 查看该构造方法上是否存在@Autowired,@Value,@Inject注解,
// 或者看代理类的父类中对应的构造方法上是否存在@Autowired,@Value,@Inject注解
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
// 如果当前类是cglib生成的代理类,则获取其父类,否则返回class本身
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
// 获取带有参数的构造函数
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
//继续寻找@Autowired,@Value,@Inject注解
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
// ann != null说明构造方法上存在注解
if (ann != null) {
// requiredConstructor:表示程序员手动指明一定要使用的构造方法
// 所以如果有多个构造方法上都写了@Autowired注解,如果其中有一个@Autowired注解的required属性为true的情况下,就会报错。
if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
// 查看@Autowired的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中存的是加了@Autowired注解的构造方法
candidates.add(candidate);
}
// 如果当前构造方法上不存在@Autowired,并且是无参构造方法,则记录该无参构造方法
// 所以我们可以方法,在遍历构造方法时,其实只关心无参构造方法和加了@Autowired注解的构造方法
else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
// 如果存在添加了@Autowired的构造方法
// candidates分为两种情况,要么candidates中包含一个required等于true的构造方法
// 要么candidates中包含一个或多个required等于false的构造方法
if (!candidates.isEmpty()) {
// 如果没有指定required为true的构造方法,那么就把无参构造方法添加到candidates中去,后续一起进行推断
if (requiredConstructor == null) {
if (defaultConstructor != null) {
// 那么就把无参的构造方法添加进去
candidates.add(defaultConstructor);
}
// 如果没有指定required为true的构造方法,并且也没有无参的构造方法,并且只有一个构造方法
else if (candidates.size() == 1 && logger.isInfoEnabled()) {
// 给一个提示,没有无参的构造方法,然后又只有一个@Autowired(required=false)的构造方法
// 所以其实一定会用这个构造方法,所以打印一个日志,告诉程序员,你其实可以把required改为true
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到了这里存在两种情况,要么只有一个@Autowired(required=true)的构造方法,
// 要么多个@Autowired(required=false)+一个无参构造方法
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
// 不存在加@Autowired注解的构造方法,那么则判断是不是只有一个有参的构造方法,如果是则返回
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
// 不管
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};
}
// 如果没有构造方法上添加了@Autowired注解,并且有多个构造方法,并且没有primaryConstructor
else {
// 返回一个空的Constructor数组,表示没有推断出来构造方法
candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
// 要么返回一个required=true的构造方法
// 要么返回多个requreid=false+无参的构造方法
// 要么返回唯一的一个有参的构造方法
// 如果只有一个无参的构造方法,这里会返回null,外层逻辑会默认使用无参构造方法进行实例化
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
总结
这段代码的逻辑比较绕,我们来慢慢理一下:
- 1、首先从缓存中判断当前 beanName 是否已经在
lookupMethodsChecked
中存在,如果存在就不需要再次解析了。否则,判断当前 Class 类中方法上是否包含@Lookup
注解,存在则将方法的信息封装为LookupOverride
对象加载到mbd中。 - 2、从缓存中获取当前 bean 中可用的构造方法,如果存在直接返回。
- 3、获取当前类所有的构造方法,并将值赋予
rawCandidates
变量。如果当前类是 cglib 生成的代理类,则获取其父类。 - 4、遍历每个构造方法,判断构造方法上是否包含
@Autowired
注解。 -
- A:存在:判断
required
属性值是否为 true,为 true 时,将当前构造方法赋值给requiredConstructor
属性。然后将当前构造方法添加到candidates
属性中。
- A:存在:判断
-
- B:不存在:判断当前构造方法的参数是否为 0 个;将当前构造方法赋值给
defaultConstructor
属性。
- B:不存在:判断当前构造方法的参数是否为 0 个;将当前构造方法赋值给
- 5、循环结束
- 6、判断
candidates
属性是否为空,表示是否存在@Autowired
注解标注的构造方法。 -
- A:不为空,当前
candidates
属性中存着带有注解的构造方法。然后判断是否存在带有required
为true的构造方法。
- A:不为空,当前
-
-
- A1:不存在,判断
defaultConstructor
属性不为 null,将defaultConstructor
属性的值添加到candidates
。defaultConstructor
属性存着无参构造方法。
- A1:不存在,判断
-
-
-
- A2:存在,将
candidates
属性赋值给candidateConstructors
,candidateConstructors
最终返回值。
- A2:存在,将
-
-
- B:为空,也就是不存在加
@Autowired
注解的构造方法。则判断rawCandidates
的大小是否等于1,并且参数个数大于是否0,这表示只有一个有参的构造方法,如果是则将值赋给candidateConstructors
属性。否则将空数组赋值给candidateConstructors
属性。
- B:为空,也就是不存在加
- 7、判断
candidateConstructors
属性的长度是否大于 0 ,大于则返回candidateConstructors
,否则返回 null。
图
图来源于
图灵学院
。
实例
既然上述逻辑比较麻烦。这里就举几个例子,便于更好更快的理解它。小玩意,我还能不懂你了~。
准备工作
- 创建两个类,一个 A 类,一个 B 类。
//A 类
public class A {
String name;
private B b;
public A() {
System.out.println("使用无参构造方法实例化");
}
public A(String name) {
System.out.println("使用 name 有参构造方法实例化");
this.name = name;
}
public A(B b) {
System.out.println("使用 B 有参构造方法实例化=" + b);
this.b = b;
}
public A(String name, B b) {
System.out.println("使用 name + B 有参构造方法实例化 ="+ b);
this.name = name;
this.b = b;
}
}
//B 类
public class B {
}
- 配置文件
<bean class="com.gongj.determineConstructors.A" id="aa"></bean>
<bean class="com.gongj.determineConstructors.B" id="bb"></bean>
<context:component-scan base-package="com.gongj.create"></context:component-scan>
- 启动类
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config5.xml");
A a = (A)context.getBean("aa");
System.out.println(a);
}
实例一
类中有两个@Autowired
标注的方法,其required
属性一个为true
,一个为false
。
在 A 类的A(B b)
构造方法A(String name)
构造方法上加上 @Autowired
注解,其required
属性一个为true
,一个为false
。
@Autowired(required = false)
public A(String name) {
System.out.println("使用 name 有参构造方法实例化");
this.name = name;
}
@Autowired
public A(B b) {
System.out.println("使用 B 有参构造方法实例化=" + b);
this.b = b;
}
各位小伙伴们可以猜猜会发生什么!猜不出来的可以再上去看看源码逻辑。小杰就先将答案说了,会抛出异常。
直接启动Main
,控制台会打印如下日志:
一个类中如果有构造方法上写了
@Autowired
注解,并且required
属性为 true
的情况下,类中的其他构造方法上不能再写@Autowired
注解了。如果写了启动时会报错。
实例二
类中有多个@Autowired
标注的方法并且required
属性都为 false
。
将 A(B b)
构造方法上的 required
属性修改为 false
。也就是说类中有两个被@Autowired
标注的方法并且required
属性都为 false
。
@Autowired(required = false)
public A(B b) {
System.out.println("使用 B 有参构造方法实例化=" + b);
this.b = b;
}
各位小伙伴们可以再猜猜结果是什么!
可以看到一共找出来了三个构造方法。两个被
@Autowired
标注的方法加上无参构造方法。那 Spring
会使用谁呢?启动Main
方法,控制台打印如下:
使用 B 有参构造方法实例化=com.gongj.determineConstructors.B@2a556333
com.gongj.determineConstructors.A@32d2fa64
可以看使用的是 A(B b)
构造方法。为什么呢?这里简单的讲一下,如果要理解,那需要各位小伙伴去打DeBug实践了。
-
既然我们从这一步找到了构造方法。那就会调用有参构造方法进行实例化Bean(autowireConstructor())。
-
然后对构造方法进行排序。按构造方法的参数个数降序排序,先排序public构造函数,参数降序排列;然后排序非public 的构造函数,参数降序排列。
-
然后循环遍历构造方法。
-
再拿到每个构造方法的参数列表,循环参数列表。根据参数类型、参数名称首先与
bean
标签里constructor-arg
指定的值进行匹配、转换,转换不了抛出异常。如果constructor-arg
没有指定值,则进行构造方法自动推断,说的直白点,就是去创建Bean或者获取Bean。就拿实例二,我们拿到了三个构造方法,然后进行排序,顺序为
A(B b)
->A(String name)
->A()
。然后循环每个构造方法;拿到每个构造方法的参数列表,循环参数列表。根据参数类型、参数名称首先与bean
标签里constructor-arg
指定的值进行匹配;上述例子肯定不会与constructor-arg
匹配成功,因为都没有。那就会走构造方法自动推断。-
A(B b)
构造方法,就会去创建Bean或者获取Bean,并且能创建成功或者获取到Bean。也就是代表该构造方法匹配成功,但可能不是最适合的,先将信息存起来,继续循环其他构造方法。// 将当前构造方法和所找到参数值作为待使用的,遍历下一个构造方法 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; }
-
A(String name)
构造方法,这推断不了,结束本次循环。 -
A()
:无参构造方法,都不会被推断,直接结束循环。if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) { // 进入这里说明已经找到选用的构造函数 且找到的构造函数参数个数大于当前遍历的,则不用继续遍历了 // PS:上面已经按照参数个数降序排列了,首先遍历的肯定是参数个数多的 break; }
-
我们修改一下代码,在 xml 文件增加如下代码,给 A Bean 增加constructor-arg
标签。
<bean class="com.gongj.determineConstructors.A" id="aa">
<constructor-arg index="0" value="gggg"></constructor-arg>
</bean>
再次猜测,控制台会打印什么?
使用 name 有参构造方法实例化
com.gongj.determineConstructors.A@38bc8ab5
A(B b)
构造方法首先会与constructor-arg
指定的值进行匹配、转换,但这根本就转换不了,String
转换为 B
对象,抛出异常,结束本次循环。然后再与A(String name)
构造方法比较。
实例三
类中只有一个构造方法的时候。
实例三需要修改代码,首先增加一个 C
类并配置相关配置。
public class C {
}
//相关配置
<bean class="com.gongj.determineConstructors.C" id="cc"></bean>
修改 A
类中代码如下,可以看到 A
类中增加了一个 C
属性,并类中只有一个构造方法A(C c, B b)
,该构造方法上并没有标注@Autowired
注解。
public class A {
String name;
private C c;
private B b;
// public A() {
// System.out.println("使用无参构造方法实例化");
//
// }
//@Autowired(required = false)
// public A(String name) {
// System.out.println("使用 name 有参构造方法实例化");
// this.name = name;
// }
//@Autowired(required = false)
// public A(B b) {
// System.out.println("使用 B 有参构造方法实例化=" + b);
// this.b = b;
// }
//@Autowired(required = false)
// public A(String name, B b) {
// System.out.println("使用 name + B 有参构造方法实例化 ="+ b);
//
// this.name = name;
// this.b = b;
// }
public A(C c, B b) {
System.out.println("使用 C + B 有参构造方法实例化 ="+ b);
this.c = c;
this.b = b;
}
}
启动控制台打印如下:
使用 C + B 有参构造方法实例化 =com.gongj.determineConstructors.B@17d677df
com.gongj.determineConstructors.A@42f93a98
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。
转载自:https://juejin.cn/post/7081553925190467614