大多数人只是停留在对 Spring 的简单应用上,所以一般也不会了解到 Spring 的构造方法注入。
其实在 Spring 的官网中明确说到:
所以,既然 Spring 官网都那么说了,你要是连构造方法注入都不好好学习,那可就有点对不起自己啦。
既然要学习 Spring 的构造方法注入,我们先不研究源码,我们先从基本的使用开始:
我先给出一个 A 类,里面有各种各样的构造方法。
然后,你可以自己先思考,Spring 会调用哪个构造方法。
然后,看我的测试,看看你想的和 Spring 想的,到底一样不一样!
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
public A(String str) {
System.out.println("str");
}
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, String str) {
System.out.println("B,C,str");
}
}
我要往 Spring 容器中添加一个 B 一个 C,这样才能够给构造方法传参:
@Component
public class B {
}
@Component
public class C {
}
这里我使用注解驱动,初始化 Spring 容器:
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
}
不知道你猜对了没有,即使猜错了也不要紧,我会慢慢分析的;
不过,假设你侥幸猜对了,那也不要高兴,看看能不能接住面试官的连环轰炸!
@Component
public class A {
public A() {
System.out.println("无参");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(String str) {
System.out.println("str");
}
}
@Component
public class A {
public A(B b) {
System.out.println("B");
}
}
@Component
public class A {
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
}
@Component
public class A {
public A(B b) {
System.out.println("B");
}
public A(String str) {
System.out.println("str");
}
}
想来你慢慢地已经开始自己发现规律了。
现在,我继续增加新的条件:
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
// 增加@Autowired注解
@Autowired
public A(C c) {
System.out.println("C");
}
public A(String str) {
System.out.println("str");
}
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, String str) {
System.out.println("B,C,str");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
public A(String str) {
System.out.println("str");
}
// 增加@Autowired注解
@Autowired
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, String str) {
System.out.println("B,C,str");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
// 增加@Autowired注解
@Autowired
public A(String str) {
System.out.println("str");
}
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, String str) {
System.out.println("B,C,str");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
@Autowired
public A(B b) {
System.out.println("B");
}
@Autowired
public A(C c) {
System.out.println("C");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
@Autowired(required = false)
public A(B b) {
System.out.println("B");
}
@Autowired
public A(C c) {
System.out.println("C");
}
}
@Component
public class A {
public A() {
System.out.println("无参");
}
@Autowired(required = false)
public A(B b) {
System.out.println("B");
}
@Autowired(required = false)
public A(C c) {
System.out.println("C");
}
}
@Component
public class A {
@Autowired(required = false)
public A() {
System.out.println("无参");
}
@Autowired(required = false)
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
}
是不是发现,一开始也没有想到?
当然,现在你也应该可以开始慢慢找到规律了。
不过,你以为这就结束了吗?
面试官一定会给你继续连环轰炸!
现在,新增一个类D:
public class D implements I {
}
public interface I {
}
下面,给 xml 配置文件中加入如下配置,也就是给 a 设置基于构造方法的自动注入。
<bean id="a" class="com.jiang.bean.A" autowire="constructor">
</bean>
<bean id="b" class="com.jiang.bean.B">
</bean>
<bean id="c" class="com.jiang.bean.C">
</bean>
<bean id="d" class="com.jiang.bean.D">
</bean>
然后依据 xml 配置初始化容器:
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
}
下面继续进行判断:
public class A {
@Autowired
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
public A(D d) {
System.out.println("D");
}
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, D d) {
System.out.println("B,C,D");
}
}
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
}
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
}
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(C c) {
System.out.println("C");
}
public A(D d) {
System.out.println("D");
}
}
public class A {
public A() {
System.out.println("无参");
}
public A(C c) {
System.out.println("C");
}
public A(I i) {
System.out.println("I");
}
}
public class A {
public A() {
System.out.println("无参");
}
public A(D d) {
System.out.println("D");
}
public A(I i) {
System.out.println("I");
}
}
是不是如果我不给你提,你打死也想不到,Spring 光一个构造方法,就有这么多的条件要去判断,可以衍生出这么多的题给你做!!!
看到这里,我想你也差不多可以自己总结出一些规律。
但是,如果用 @Autowired 指定了多个,或者开启了构造方法的自动注入,那么选出的最合适的又是哪个?
根据现象来看:
说了这么多,不过,这些都是现象对不对,我还没有给你们分析源码。
那么,接下来,我们就要先找到 Spring 推断构造方法的代码。
既然什么时候会推断构造方法呢?
那肯定是要创建对象的时候,要去推断构造方法(比较构造方法是用来创建对象的)。
于是,我们在 Spring 中找到 createBeanInstance(beanName, mbd, args); 这个方法。
点进去,我们可以看到:
在 determineConstructorsFromBeanPostProcessors 这个方法处,返回了一个 构造方法数组;
而且,这个方法的名字,翻译成中文,就叫决定构造方法。
那么,很有可能,就是在这个方法中,推断出了要使用的构造方法。
于是,我们 debug 一下:
此时采用注解驱动开发,且 A 的代码为:
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
public A(B b, C c) {
System.out.println("B,C");
}
}
然后,开始 debug:
你会发现,它返回了一个 null。
是不是觉得很奇怪,怎么它推断不出构造方法吗?
按照道理,此时没有使用自动构造方法注入,那应该返回一个无参构造方法才对,可是怎么只有一个 null?
于是,我让你们抱着怀疑的心态,再试一试:
这回,只有一个无参构造方法了,Spring 总能推断的出来吧。
@Component
public class A {
public A() {
System.out.println("无参");
}
}
总感觉不太对劲是吧,于是,再让你抱着怀疑的心态,再给你测试测试:
@Component
public class A {
public A(B b) {
System.out.println("B");
}
}
然后,你就会发现 Spring 把构造方法找出来了。
你肯能有很多问号,不过没关系,我再给你加一个问号:
@Component
public class A {
public A() {
System.out.println("无参");
}
public A(B b) {
System.out.println("B");
}
@Autowired
public A(B b, C c) {
System.out.println("B,C");
}
public A(B b, C c, D d) {
System.out.println("B,C,D");
}
}
你会发现,这次 Spring 又把构造方法找出来了,而且就是 @Autowired 指定的那个构造方法。
现在也许你很疑惑,不过不要担心,我来告诉你答案。
既然要探究 Spring 的做法,那我们就来分析源码:
---- 想了想,这次还是用源码+注释的形式吧,不会阅读代码的小伙伴,就背我的结论,面试的时候就跟面试官直接喷,说 Spring 源码是这么这么写的。。。。
---- 我给的细节很全,只要你记住了,一般不会碰到一些搞不定的面试题(我只是指推断构造方法相关的)。
我先解释一下,为什么上面的推断构造方法,给出的返回值为 null。
因为,如果最后没有找出构造方法的话,Spring 就会使用默认无参构造方法。
所以,返回 null,就表示,要使用无参构造方法,Spring 会在后面的代码进行调用无参构造方法进行创建对象。
注意!!!:这只是在没有开启构造方法自动注入的情况下!!!
假设开启了构造方法的自动注入,在这里返回 null 的时候,还会接着继续找构造方法。
---- 如果你直接看源码,可能比较费力,我先给你描述一遍流程,你再阅读源码,就会轻松很多。
---- 所有和 Kotlin 有关的代码都不去管,我没学过 Kotlin,也不用它做开发。
下面,我们就来看推断构造方法的源代码:
(虽然我写了很多注释,不过如果发现看不下去了,那至少背上面的流程,和背结论)
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {
// Let's check for lookup methods here...
// lookup相关我直接省略
// Quick check on the concurrent map first, with minimal locking.
// 这里是用于缓存,如果推断过构造方法了,那么就会保存在一个Map中,
// 那么下一次来创建对象的时候,就直接返回Map中保存的,就不用再次推断构造方法了。
// 从Map中取
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
// 如果Map中取出的为null,说明没有创建过对象,此时要推断构造方法
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) {
抛异常
}
// 这个list用于存放加了@Autowired注解的构造方法
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// 加了@Autowired注解的构造方法,如果required==true,就用这个变量存放
Constructor<?> requiredConstructor = null;
// 用于存放默认无参构造方法
Constructor<?> defaultConstructor = null;
// 这个primaryConstructor是用来返回Kotlin类的主要构造方法
// 但是如果是Java类,则只会返回null
// 和Kotlin相关,所以不研究
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
// 不考虑合成方法,此处忽略
int nonSyntheticConstructors = 0;
// 遍历所有的构造方法
for (Constructor<?> candidate : rawCandidates) {
// 如果是合成的构造方法(不要管这个)
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
// 因为用的是Java,所以这个primaryConstructor一定为null,所以不用管
else if (primaryConstructor != null) {
continue;
}
// 如果构造方法加了@Autowired,就会获取到ann
AnnotationAttributes ann = findAutowiredAnnotation(candidate);
if (ann == null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
// 因为,如果有继承关系,那么父类可能会加@Autowired注解
// 所以尝试获取父类的构造方法
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
// 从父类那里寻找是否有添加@Autowired注解
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
// ann != null,说明这个构造方法加了@Autowired注解
// 根据抛异常的规则,如果有多个构造方法加了@Autowired注解,
// 只要有required=true,就要抛异常。
// 所以如果加了多个注解,那么所有的注解都要required=false,才不会抛异常
if (ann != null) {
// requiredConstructor != null,
// 说明之前也有构造方法加了@Autowired注解,并且required=true,那就要抛异常
if (requiredConstructor != null) {
抛异常
}
boolean required = determineRequiredStatus(ann);
if (required) {
// 如果不为空,那么就说明之前有构造方法加了@Autowired注解,就要抛异常
if (!candidates.isEmpty()) {
抛异常
}
// 这时,这个requiredConstructor就会被赋值
requiredConstructor = candidate;
}
// 那么这个加了@Autowired注解的构造方法就会存入candidates这个list
candidates.add(candidate);
}
// 如果参数是0,那么就是无参构造方法,就赋值给defaultConstructor
else if (candidate.getParameterCount() == 0) {
// defaultConstructor就是在这里被找出赋值
defaultConstructor = candidate;
}
}
// 以为上面找出有@Autowired注解的构造方法都被添加到了candidates这个List中
// 所以此时不为空,说明有加了@Autowired注解的构造方法
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
// 如果@Autowired注解全是required=false,且额外有无参构造方法,就再加一个无参构造方法,
// 所以candidates就一定有多个构造方法
// 反之,要是required=true,那就不会进if
// 那candidates中就只会有那一个确定的构造方法
if (requiredConstructor == null) {
if (defaultConstructor != null) {
candidates.add(defaultConstructor);
}
else if (candidates.size() == 1 && logger.isInfoEnabled()) {
打印日志
}
}
// List转化为数组
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
// 上面完成了所有构造方法的判断
// 如果只有一个构造方法,并且参数>0
// 就是只有一个有参构造方法
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
// 返回的数组,就是那一个构造方法
candidateConstructors = new Constructor<?>[] {
rawCandidates[0]};
}
// 这里由于不牵扯kotlin,所以primaryConstructor一定为null,所以不用考虑
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};
}
// 有一个无参构造方法,或者有多个构造方法
// candidateConstructors就会是长度为0的数组
// 所以在只有无参构造方法的时候,会返回null,
// 如果有多个构造方法,只要没加@Autowired注解,也返回null
else {
candidateConstructors = new Constructor<?>[0];
}
// 最后加入缓存
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
// 如果数组长度为0,就返回空
// 如果长度不为0,说明有@Autowired注解的方法,
// 或者也可能是只有一个有参构造方法
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
这就是 Spring 第一次推断构造方法,不过只是一个普通的,不考虑自动注入,配置参数的推断!
来看总结的返回结果:
不过,由于第一次推断,是不考虑自动注入,配置信息的。
所以,还可能有第二次的判断,最终决定。
// 这里第一次推断了构造方法
// 如果为空,说明有多个构造方法,或者只有一个默认构造方法
// 如果不为空,那就可能就是只有一个有参构造方法,那么就会执行这个构造方法
// 也可能只有一个@Autowired注解,并且required=true
// 也有可能返回了多个构造方法,说明有构造方法加了@Autowired注解,并且全部都是required=false
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 如果不为空的话,就会进入if来进行对象的创建
// 不过如果为空,但是自动注入模型为构造方法自动注入,也会调用方法再次决定构造方法去执行
// 或者,如果配置中指定了构造方法的参数,那么也会进入该方法
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
那么,我们就要接着研究,autowireConstructor 这个方法。
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
可以发现,这个方法,就是调用了另一个方法,我们继续点进去:
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
// 初始化bw,不要用管
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
// 最后被确定要使用的构造方法
Constructor<?> constructorToUse = null;
// 存放构造方法使用的参数
ArgumentsHolder argsHolderToUse = null;
// 最后被确定使用的参数
Object[] argsToUse = null;
// explicitArgs通过getBean方法传入,如果getBean方法调用的时候指定方法参数那么直接使用
// 一般只有原型会手动getBean,可以指定传入参数。
// 而单例bean在容器刷新时就有了,所以getBean传入参数没什么作用,不会再来创建
// 所以,这里就是null,我们继续往后看
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
// 如果在getBean方法没有指定则尝试从配置文件中解析,xml文件可以指定构造方法的参数
Object[] argsToResolve = null;
//尝试从缓存中获取
synchronized (mbd.constructorArgumentLock) {
// 已经解析过的构造方法或工厂方法,一般只有原型才会不为null,第一次创建解析,以后就不用再解析了
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached constructor...
// 直接使用之前解析过的参数
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
// 配置的构造函数参数(因为配置的参数value都是String,所以需要解析)
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
// 解析参数类型
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
// 说明没有用getBean传参,也没有在配置文件中配置
// 那么就会在后面开始选择构造方法开始执行
if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
// 这里是之前第一次选出的构造方法数组
Constructor<?>[] candidates = chosenCtors;
// ==null说明之前无法决定构造方法,说明需要构造方法自动注入模型来推断构造方法
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
// 如果能访问非public的构造方法,那就获取所有的包括非public的构造方法,
// 否则,只获取public的构造方法
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
抛异常
}
}
// candidates中只有一个构造方法,说明可能之前已经选出了构造方法
// 说明可能只有一个有参构造方法,或者有一个@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;
// 参数最小要有多少个
int minNrOfArgs;
// 我们一般不会调用getBean传参创建时使用
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
// 提取配置文件中的配置的构造函数参数
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
// 用于承载解析后的构造函数参数的值
resolvedValues = new ConstructorArgumentValues();
// 需要的最小参数值,配置了的参数的个数
// 因为配置了那么多参数了,总不能还调用一个参数比配置的参数个数少的构造方法吧
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 排序给定的构造函数,public在前,非public在后
// 按照构造方法的优先级进行排序
// 由于这是Spring弄得优先级很复杂,不适合阅读,不过我们通过之前的测试代码,也发现了规律
AutowireUtils.sortConstructors(candidates);
// 这个变量用于记录最小的差异权重
int minTypeDiffWeight = Integer.MAX_VALUE;
// 如果有权重相等,那么放到这个模棱两可的集合中
Set<Constructor<?>> ambiguousConstructors = null;
// 发生异常不抛出,先存入这个List中
LinkedList<UnsatisfiedDependencyException> causes = null;
// 这里就会按照刚才的优先级顺序进行遍历
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
// 如果之前选用的构造方的的参数的个数 > 当前构造方法的参数个数,就break终止循环
// 因为排序有顺序的,参数个数多的排在最前面
// 所以,一般只要构造方法符合要求,Spring会自动选用最长的构造方法
if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {
// Already found greedy constructor that can be satisfied ->
// do not look any further, there are only less greedy constructors left.
break;
}
if (paramTypes.length < minNrOfArgs) {
// 构造方法参数个数还没有达到最小参数个数要求,
// 因为至得和配置的参数个数一样多
continue;
}
ArgumentsHolder argsHolder;
// 如果封装的参数对象不为空
if (resolvedValues != null) {
try {
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
// 获取参数名称
paramNames = pnd.getParameterNames(candidate);
}
}
// 获取需要的参数值
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 LinkedList<>();
}
// 将异常存入List中
causes.add(ex);
continue;
}
}
else {
// Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
// 获取差异权重值
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
// 如果它的类型差异权重更小,就选用这个构造方法和参数,
// 然后把当前最小的类型差异权重设置为它的权重值
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
// 如果它的类型差异权重和之前的一个最小的类型差异权重值相等
// 就把这个构造方法添加到一个模棱两可构造方法set中,表示这些构造方法无法根据权重选出最优
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
// 加入模棱两可集合
ambiguousConstructors.add(candidate);
}
}
// 完成循环遍历后
// 如果都没找到要用的构造方法,那就得抛异常
if (constructorToUse == null) {
if (causes != null) {
UnsatisfiedDependencyException ex = causes.removeLast();
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
throw ex;
}
抛异常
}
// 如果模棱两可构造方法集合不为空,并且不是宽松解析构造方法的话,就要抛异常
// (一般默认是宽松的,所以遇到模棱两可直接使用排在前面的)
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
抛异常
}
// 都不为空,说明已经解析到了要使用的构造方法
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;
}
源码就这么多,可以说确实没有什么难度,我对第二次的方法做一个小小的总结:
构造方法的选择,如果你阅读到了这里,那你一定会收益匪浅。
很多时候,由于开发者只写了几行代码,就把 Spring 跑起来了,就认为已经学会了。
其实 Spring 在背后还默默地做了很多很多事。
所以,对于框架的学习,绝对不能只稍稍使用,我们还应该,多去阅读源码,理清其中的实现思路,以及提供给使用者的扩展点。
这样,才能更好地,对框架进行使用和扩展。
当然,Spring 由于除了构造方法注入,还会有基于 setter 的注入,关于自动注入,还有很多人抱有误解。
所以,笔者也建议你再去看一下我的这篇有关自动注入的文章,相信你会获益匪浅!
99%的人把Spring的自动注入理解错了!(范例→源码证明)
当然,最重要的,先把我提到的知识点,背过!!!