2023年6月20日发(作者:)

Java程序员笔记,Spring构造器注⼊原理细节分析JavaSpring⾼级进阶 2019-01-22 17:31:28开篇Spring IOC是⾯试常问的知识点。本⽂讲述了从⾃定义注册Bean开始,到解析IOC容器初始化Bean的判断的⼀系列过程,从现象看本质,分析了Spring中的构造器注⼊的原理,并且分析了各种情况,相信理解了的读者将来遇到这类的别的问题可以独⽴思考出答案。1. ⽰例先来看⼀个例⼦,看看什么是构造器注⼊。这⾥我写了⼀个类,分别有三个构造器,⼀个是注⼊⼀个Bean的构造器,⼀个是注⼊两个Bean的构造器,还有⼀个⽆参构造器:

Model类User与Role我就不贴代码了,分别是有两个变量,⼀个id,⼀个name。然后就是Spring的配置⽂件:

注意,如果需要使⽤构造器注⼊,需要 此⾃定义标签开启(关于⾃定义标签,在本⼈往期的Spring系列中有详细介绍),具体作⽤后⾯再作分析。那么,该类三个构造器,Spring会使⽤哪个构造器初始化ConstructorAutowiredTest这个Bean呢?写个测试便知:

执⾏test⽅法,控制台打印:

从这⾥我们可以看出来,此时三个构造器中Spring使⽤了⽆参构造器。我们换⼀个⽅式,将⽆参构造器注释掉,看看Spring到底会使⽤哪个构造器呢?同样执⾏test⽅法测试,控制台打印:

此时控制台报错,⼤致意思是Bean的实例化失败了,没有⽆参的构造器⽅法调⽤。此时有个疑问,明明构造器中的参数都是Bean,为什么不能初始化,⼀定要使⽤⽆参的构造器呢?是否是因为有两个构造器的原因?此时我们再注释掉任意⼀个构造函数,使测试类只有⼀个带参构造函数:

再次运⾏测试类,控制台打印:

如果是注释掉第⼆个构造函数,则结果是两个对象都有。从这⾥我们可以看出,如果只有⼀个构造器,且参数为IOC容器中的Bean,将会执⾏⾃动注⼊。这⾥⼜有⼀个疑问,这也太不科学了吧,强制⽤户⼀定只能写⼀个构造器?这时我们猜想@Autowired注解是否能解决这种问题?来试试吧。我们将构造器全部解除注释,将第三个构造器打上@Autowired注解:

运⾏测试,控制台打印:

不出所料,@Autowired注解可以解决这种问题,此时Spring将使⽤有注解的构造函数进⾏Bean的初始化。那么,如果有两个@Autowired注解呢?结果肯定是报错,因为@Autowired的默认属性required是为true的,也就是说两个required=true的构造器,Spring不知道使⽤哪⼀个。但如果是这样写的话:

结果是怎样的呢?看看控制台打印:

使⽤参数最多的那⼀个构造器来初始化Bean。⼜如果两个有参构造器顺序调换⼜是怎样的呢?⼀个required为false⼀个为true,结果⼜是怎样的呢?这⾥直接给出答案,顺序调换依然使⽤多参数构造器,并且required只要有⼀个true就会报错。有兴趣的读者可以⾃⼰试试,下⾯将深⼊源码分析构造器注⼊的过程,相信上述所有疑问都能得到解答。疑问点⼩结从现象看本质,我们从上⾯的例⼦中,⼤致可以得到以下⼏个疑问:1. 为什么写三个构造器(含有⽆参构造器),并且没有@Autowired注解,Spring总是使⽤⽆参构造器实例化Bean?2. 为什么注释掉两个构造器,留下⼀个有参构造器,并且没有@Autowired注解,Spring将会使⽤构造器注⼊Bean的⽅式初始化Bean?3. 为什么写三个构造器,并且在其中⼀个构造器上打上**@Autowired注解,就可以正常注⼊构造器?并且两个@Autowired注解就会报错**,⼀定需要在所有@Autowired中的required都加上false即可正常初始化等等?或许有⼀些没有提到的疑问点,但⼤致就这么多吧,举多了也没⽤,因为在下⾯深⼊源码的分析中读者若是可以理解,关于此类的⼀系列问题都将可以⾃⼰思考得出结果,得到可以举⼀反三的能⼒。2. 依赖注⼊伊始在开头,我们有提到,如果需要构造器注⼊的功能的话,我们需要在xml配置中写下这样⼀段代码:

如果有看过本⼈⾃定义标签或是有⾃定义标签基础的读者,第⼀反应应该是先看看⾃定义标签的context对应的命名空间是哪个:

我们全局搜索此命名空间(http后需要加⼀个斜杠符号)得到⼀个rs⽂件:/schema/context=tNamespaceHandler这⾥说明了此命名空间对应的NamespaceHandler,进⼊此类看看其init⽅法:

我们的⾃定义标签是叫作 “annotation-config" ,所以对应的解析器是AnnotationConfigBeanDefinitionParser这个类,进⼊这个类的parse⽅法:

这⾥只需要关注registerAnnotationConfigProcessors⽅法,看看到底需要注册哪些Bean:

到这⾥我们可以知道,此⾃定义标签注册了⼀个AutowiredAnnotationBeanPostProcessor类的Bean到IOC容器。那么此类是⼲什么⽤的呢?3. 初始化Bean我们将思路转到IOC容器初始化Bean的流程中来,在getBean⽅法获取⼀个Bean的时候,IOC容器才开始将Bean进⾏初始化,此时会先实例化⼀个Bean,然后再对Bean进⾏依赖注⼊,然后执⾏⼀系列初始化的⽅法,完成Bean的整个初始化过程。⽽本⽂讨论的构造器注⼊,则是在实例化Bean的过程中进⾏的。在AbstractAutowireCapableBeanFactory类中的doCreateBean⽅法获取Bean的开始,将调⽤createBeanInstance⽅法进⾏Bean的实例化(选择Bean使⽤哪个构造器实例化):

到这⾥我们可以知道,determineConstructorsFromBeanPostProcessors⽅法将选择是否有适合的⾃动注⼊构造器,如果没有,将使⽤⽆参构造器实例化,关键就在这个⽅法中,是如何判断哪些构造器使⽤⾃动注⼊的呢:

这段代码告诉我们,这⾥会使⽤SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor进⾏判断,我们回顾⼀下上⾯的依赖注⼊伊始的时候我们说的⾃定义标签注册的类的结构:

有看过本⼈关于Sprng扩展篇的⽂章或是有Spring扩展点基础的读者,应该可以知道,若是注册⼀个BeanPostProcessor到IOC容器中,在AbstractApplicationContext中的refresh⽅法会对这些类型的Bean进⾏处理,存放在⼀个集合,此时getBeanPostProcessors⽅法就可以获取到所有BeanPostProcessor集合,遍历集合,便可以调⽤到我们⾃定义标签中注册的这个类型的Bean。当然,SmartInstantiationAwareBeanPostProcessor类型的Bean有很多,但依赖注⼊是使⽤上述这个Bean来完成的。回到主线,下⾯会调⽤它的determineCandidateConstructors⽅法进⾏查找对应构造器(核⼼⽅法):public Constructor[] determineCandidateConstructors(Class beanClass, final String beanName) throws BeanCreationException { //略.. // Quick check on the concurrent map first, with minimal locking. //先查找缓存 Constructor[] candidateConstructors = (beanClass); //若是缓存中没有 if (candidateConstructors == null) { // Fully synchronized //同步此⽅法 synchronized (ateConstructorsCache) { candidateConstructors = (beanClass); //双重判断,避免多线程并发问题 if (candidateConstructors == null) { Constructor[] rawCandidates; try { //获取此Bean的所有构造器 rawCandidates = laredConstructors(); } } catch (Throwable ex) { throw new BeanCreationException(beanName, "Resolution of declared constructors on bean Class [" + e() + "] from ClassLoader [" + ssLoader() + "] failed", ex); } //最终适⽤的构造器集合 List> candidates = new ArrayList<>(); //存放依赖注⼊的required=true的构造器 Constructor requiredConstructor = null; //存放默认构造器 Constructor defaultConstructor = null; Constructor primaryConstructor = imaryConstructor(beanClass); int nonSyntheticConstructors = 0; for (Constructor candidate : rawCandidates) { if (!hetic()) { nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue; } //查找当前构造器上的注解 AnnotationAttributes ann = findAutowiredAnnotation(candidate); //若没有注解 if (ann == null) { Class userClass = rClass(beanClass); if (userClass != beanClass) { try { Constructor superCtor = laredConstructor(ameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) { // Simply proceed, no equivalent superclass } } } //若有注解 if (ann != null) { //已经存在⼀个required=true的构造器了,抛出异常 if (requiredConstructor != null) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: " + candidate + ". Found constructor with 'required' Autowired annotation already: " + requiredConstructor); } //判断此注解上的required属性 boolean required = determineRequiredStatus(ann); //若为true if (required) { if (!y()) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: " + candidates + ". Found constructor with 'required' Autowired annotation: " + candidate); } //将当前构造器加⼊requiredConstructor集合 requiredConstructor = candidate; } //加⼊适⽤的构造器集合中 (candidate); } //如果该构造函数上没有注解,再判断构造函数上的参数个数是否为0 else if (ameterCount() == 0) { //如果没有参数,加⼊defaultConstructor集合 defaultConstructor = candidate; defaultConstructor = candidate; } } //适⽤的构造器集合若不为空 if (!y()) { // Add default constructor to list of optional constructors, as fallback. //若没有required=true的构造器 if (requiredConstructor == null) { if (defaultConstructor != null) { //将defaultConstructor集合的构造器加⼊适⽤构造器集合 (defaultConstructor); } else if (() == 1 && Enabled()) { ("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: " + (0)); } } //将适⽤构造器集合赋值给将要返回的构造器集合 candidateConstructors = y(new Constructor[0]); } //如果适⽤的构造器集合为空,且Bean只有⼀个构造器并且此构造器参数数量⼤于0 else if ( == 1 && rawCandidates[0].getParameterCount() > 0) { //就使⽤此构造器来初始化 candidateConstructors = new Constructor[] {rawCandidates[0]}; } //如果构造器有两个,且默认构造器不为空 else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null && !(defaultConstructor)) { //使⽤默认构造器返回 candidateConstructors = new Constructor[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { candidateConstructors = new Constructor[] {primaryConstructor}; } else { //上述都不符合的话,只能返回⼀个空集合了 candidateConstructors = new Constructor[0]; } //放⼊缓存,⽅便下⼀次调⽤ (beanClass, candidateConstructors); } } } //返回candidateConstructors集合,若为空集合返回null return ( > 0 ? candidateConstructors : null);}从这段核⼼代码我们可以看出⼏个要点:在没有@Autowired注解的情况下:⽆参构造器将直接加⼊defaultConstructor集合中。在构造器数量只有⼀个且有参数时,此唯⼀有参构造器将加⼊candidateConstructors集合中。在构造器数量有两个的时候,并且存在⽆参构造器,将defaultConstructor(第⼀条的⽆参构造器)放⼊candidateConstructors集合中。在构造器数量⼤于两个,并且存在⽆参构造器的情况下,将返回⼀个空的candidateConstructors集合,也就是没有找到构造器。在有@Autowired注解的情况下:判断required属性:true:先判断requiredConstructor集合是否为空,若不为空则代表之前已经有⼀个required=true的构造器了,两个true将抛出异常,再判断candidates集合是否为空,若不为空则表⽰之前已经有⼀个打了注解的构造器,此时required⼜是true,抛出异常。若两者都不为空将放⼊requiredConstructor集合中,再放⼊candidates集合中。false:直接放⼊candidates集合中。判断requiredConstructor集合是否为空(是否存在required=true的构造器),若没有,将默认构造器也放⼊candidates集合中。最后将上述candidates赋值给最终返回的candidateConstructors集合。4.总结综上所述,我们可以回答开篇疑问点⼩结所总结的⼀系列问题了:为什么写三个构造器(含有⽆参构造器),并且没有@Autowired注解,Spring总是使⽤⽆参构造器实例化Bean?答:参照没有注解的处理⽅式: 若构造器只有两个,且存在⽆参构造器,将直接使⽤⽆参构造器初始化。若⼤于两个构造器,将返回⼀个空集合,也就是没有找到合适的构造器,那么参照第三节初始化Bean的第⼀段代码createBeanInstance⽅法的末尾,将会使⽤⽆参构造器进⾏实例化。这也就解答了为什么没有注解,Spring总是会使⽤⽆参的构造器进⾏实例化Bean,并且此时若没有⽆参构造器会抛出异常,实例化Bean失败。为什么注释掉两个构造器,留下⼀个有参构造器,并且没有@Autowired注解,Spring将会使⽤构造器注⼊Bean的⽅式初始化Bean?答:参照没有注解的处理⽅式: 构造器只有⼀个且有参数时,将会把此构造器作为适⽤的构造器返回出去,使⽤此构造器进⾏实例化,参数⾃然会从IOC中获取Bean进⾏注⼊。为什么写三个构造器,并且在其中⼀个构造器上打上@Autowired注解,就可以正常注⼊构造器?答:参照有注解的处理⽅式: 在最后判断candidates适⽤的构造器集合是否为空时,若有注解,此集合当然不为空,且required=true,也不会将默认构造器集合defaultConstructor加⼊candidates集合中,最终返回的是candidates集合的数据,也就是这唯⼀⼀个打了注解的构造器,所以最终使⽤此打了注解的构造器进⾏实例化。两个@Autowired注解就会报错,⼀定需要在所有@Autowired中的required都加上false即可正常初始化?答:参照有注解的处理⽅式: 当打了两个@Autowired注解,也就是两个required都为true,将会抛出异常,若是⼀个为true,⼀个为false,也将会抛出异常,⽆论顺序,因为有两层的判断,⼀个是requiredConstructor集合是否为空的判断,⼀个是candidates集合为空的判断,若两个构造器的required属性都为false,不会进⾏上述判断,直接放⼊candidates集合中,并且在下⾯的判断中会将defaultConstructor加⼊到candidates集合中,也就是candidates集合有三个构造器,作为结果返回。⾄于第四条结论,返回的构造器若有三个,Spring将如何判断使⽤哪⼀个构造器呢?在后⾯Spring会遍历三个构造器,依次判断参数是否是Spring的Bean(是否被IOC容器管理),若参数不是Bean,将跳过判断下⼀个构造器,也就是说,例如上述两个参数的构造器其中⼀个参数不是Bean,将判断⼀个参数的构造器,若此参数是Bean,使⽤⼀个参数的构造器实例化,若此参数不是Bean,将使⽤⽆参构造器实例化。也就是说,若使⽤@Autowired注解进⾏构造器注⼊,required属性都设置为false的话,将避免⽆Bean注⼊的异常,使⽤⽆参构造器正常实例化。若两个参数都是Bean,则就直接使⽤两个参数的构造器进⾏实例化并获取对应Bean注⼊构造器。在这⾥最后说⼀点,从上⾯可以看出,若想使⽤构造器注⼊功能,最好将要注⼊的构造器都打上@Autowired注解(若有多个需要注⼊的构造器,将所有@Autowired中required属性都设置为false),若有多个构造器,只有⼀个构造器需要注⼊,将这个构造器打上@Autowired注解即可,不⽤设置required属性。如果不打注解也是可以使⽤构造器注⼊功能的,但构造器数量只能为1,且代码可读性较差,读代码的⼈并不知道你这⾥使⽤了构造器注⼊的⽅式,所以这⾥我建议若使⽤构造器注⼊打上@Autowired注解会⽐较好⼀点。声明:本⽂来源于⽹络。最后如果你对技术提升很感兴趣,可以加⼊Java进阶之路来交流学习:878249276,⾥⾯都是同⾏,有资源分享包括但不限于(分布式架构、⾼可扩展、⾼性能、⾼并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。欢迎⼀到五年的⼯程师加⼊,合理利⽤⾃⼰每⼀分每⼀秒的时间来学习提升⾃⼰,不要再⽤"没有时间“来掩饰⾃⼰思想上的懒惰!趁年轻,使劲拼,给未来的⾃⼰⼀个交代!

2023年6月20日发(作者:)

Java程序员笔记,Spring构造器注⼊原理细节分析JavaSpring⾼级进阶 2019-01-22 17:31:28开篇Spring IOC是⾯试常问的知识点。本⽂讲述了从⾃定义注册Bean开始,到解析IOC容器初始化Bean的判断的⼀系列过程,从现象看本质,分析了Spring中的构造器注⼊的原理,并且分析了各种情况,相信理解了的读者将来遇到这类的别的问题可以独⽴思考出答案。1. ⽰例先来看⼀个例⼦,看看什么是构造器注⼊。这⾥我写了⼀个类,分别有三个构造器,⼀个是注⼊⼀个Bean的构造器,⼀个是注⼊两个Bean的构造器,还有⼀个⽆参构造器:

Model类User与Role我就不贴代码了,分别是有两个变量,⼀个id,⼀个name。然后就是Spring的配置⽂件:

注意,如果需要使⽤构造器注⼊,需要 此⾃定义标签开启(关于⾃定义标签,在本⼈往期的Spring系列中有详细介绍),具体作⽤后⾯再作分析。那么,该类三个构造器,Spring会使⽤哪个构造器初始化ConstructorAutowiredTest这个Bean呢?写个测试便知:

执⾏test⽅法,控制台打印:

从这⾥我们可以看出来,此时三个构造器中Spring使⽤了⽆参构造器。我们换⼀个⽅式,将⽆参构造器注释掉,看看Spring到底会使⽤哪个构造器呢?同样执⾏test⽅法测试,控制台打印:

此时控制台报错,⼤致意思是Bean的实例化失败了,没有⽆参的构造器⽅法调⽤。此时有个疑问,明明构造器中的参数都是Bean,为什么不能初始化,⼀定要使⽤⽆参的构造器呢?是否是因为有两个构造器的原因?此时我们再注释掉任意⼀个构造函数,使测试类只有⼀个带参构造函数:

再次运⾏测试类,控制台打印:

如果是注释掉第⼆个构造函数,则结果是两个对象都有。从这⾥我们可以看出,如果只有⼀个构造器,且参数为IOC容器中的Bean,将会执⾏⾃动注⼊。这⾥⼜有⼀个疑问,这也太不科学了吧,强制⽤户⼀定只能写⼀个构造器?这时我们猜想@Autowired注解是否能解决这种问题?来试试吧。我们将构造器全部解除注释,将第三个构造器打上@Autowired注解:

运⾏测试,控制台打印:

不出所料,@Autowired注解可以解决这种问题,此时Spring将使⽤有注解的构造函数进⾏Bean的初始化。那么,如果有两个@Autowired注解呢?结果肯定是报错,因为@Autowired的默认属性required是为true的,也就是说两个required=true的构造器,Spring不知道使⽤哪⼀个。但如果是这样写的话:

结果是怎样的呢?看看控制台打印:

使⽤参数最多的那⼀个构造器来初始化Bean。⼜如果两个有参构造器顺序调换⼜是怎样的呢?⼀个required为false⼀个为true,结果⼜是怎样的呢?这⾥直接给出答案,顺序调换依然使⽤多参数构造器,并且required只要有⼀个true就会报错。有兴趣的读者可以⾃⼰试试,下⾯将深⼊源码分析构造器注⼊的过程,相信上述所有疑问都能得到解答。疑问点⼩结从现象看本质,我们从上⾯的例⼦中,⼤致可以得到以下⼏个疑问:1. 为什么写三个构造器(含有⽆参构造器),并且没有@Autowired注解,Spring总是使⽤⽆参构造器实例化Bean?2. 为什么注释掉两个构造器,留下⼀个有参构造器,并且没有@Autowired注解,Spring将会使⽤构造器注⼊Bean的⽅式初始化Bean?3. 为什么写三个构造器,并且在其中⼀个构造器上打上**@Autowired注解,就可以正常注⼊构造器?并且两个@Autowired注解就会报错**,⼀定需要在所有@Autowired中的required都加上false即可正常初始化等等?或许有⼀些没有提到的疑问点,但⼤致就这么多吧,举多了也没⽤,因为在下⾯深⼊源码的分析中读者若是可以理解,关于此类的⼀系列问题都将可以⾃⼰思考得出结果,得到可以举⼀反三的能⼒。2. 依赖注⼊伊始在开头,我们有提到,如果需要构造器注⼊的功能的话,我们需要在xml配置中写下这样⼀段代码:

如果有看过本⼈⾃定义标签或是有⾃定义标签基础的读者,第⼀反应应该是先看看⾃定义标签的context对应的命名空间是哪个:

我们全局搜索此命名空间(http后需要加⼀个斜杠符号)得到⼀个rs⽂件:/schema/context=tNamespaceHandler这⾥说明了此命名空间对应的NamespaceHandler,进⼊此类看看其init⽅法:

我们的⾃定义标签是叫作 “annotation-config" ,所以对应的解析器是AnnotationConfigBeanDefinitionParser这个类,进⼊这个类的parse⽅法:

这⾥只需要关注registerAnnotationConfigProcessors⽅法,看看到底需要注册哪些Bean:

到这⾥我们可以知道,此⾃定义标签注册了⼀个AutowiredAnnotationBeanPostProcessor类的Bean到IOC容器。那么此类是⼲什么⽤的呢?3. 初始化Bean我们将思路转到IOC容器初始化Bean的流程中来,在getBean⽅法获取⼀个Bean的时候,IOC容器才开始将Bean进⾏初始化,此时会先实例化⼀个Bean,然后再对Bean进⾏依赖注⼊,然后执⾏⼀系列初始化的⽅法,完成Bean的整个初始化过程。⽽本⽂讨论的构造器注⼊,则是在实例化Bean的过程中进⾏的。在AbstractAutowireCapableBeanFactory类中的doCreateBean⽅法获取Bean的开始,将调⽤createBeanInstance⽅法进⾏Bean的实例化(选择Bean使⽤哪个构造器实例化):

到这⾥我们可以知道,determineConstructorsFromBeanPostProcessors⽅法将选择是否有适合的⾃动注⼊构造器,如果没有,将使⽤⽆参构造器实例化,关键就在这个⽅法中,是如何判断哪些构造器使⽤⾃动注⼊的呢:

这段代码告诉我们,这⾥会使⽤SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor进⾏判断,我们回顾⼀下上⾯的依赖注⼊伊始的时候我们说的⾃定义标签注册的类的结构:

有看过本⼈关于Sprng扩展篇的⽂章或是有Spring扩展点基础的读者,应该可以知道,若是注册⼀个BeanPostProcessor到IOC容器中,在AbstractApplicationContext中的refresh⽅法会对这些类型的Bean进⾏处理,存放在⼀个集合,此时getBeanPostProcessors⽅法就可以获取到所有BeanPostProcessor集合,遍历集合,便可以调⽤到我们⾃定义标签中注册的这个类型的Bean。当然,SmartInstantiationAwareBeanPostProcessor类型的Bean有很多,但依赖注⼊是使⽤上述这个Bean来完成的。回到主线,下⾯会调⽤它的determineCandidateConstructors⽅法进⾏查找对应构造器(核⼼⽅法):public Constructor[] determineCandidateConstructors(Class beanClass, final String beanName) throws BeanCreationException { //略.. // Quick check on the concurrent map first, with minimal locking. //先查找缓存 Constructor[] candidateConstructors = (beanClass); //若是缓存中没有 if (candidateConstructors == null) { // Fully synchronized //同步此⽅法 synchronized (ateConstructorsCache) { candidateConstructors = (beanClass); //双重判断,避免多线程并发问题 if (candidateConstructors == null) { Constructor[] rawCandidates; try { //获取此Bean的所有构造器 rawCandidates = laredConstructors(); } } catch (Throwable ex) { throw new BeanCreationException(beanName, "Resolution of declared constructors on bean Class [" + e() + "] from ClassLoader [" + ssLoader() + "] failed", ex); } //最终适⽤的构造器集合 List> candidates = new ArrayList<>(); //存放依赖注⼊的required=true的构造器 Constructor requiredConstructor = null; //存放默认构造器 Constructor defaultConstructor = null; Constructor primaryConstructor = imaryConstructor(beanClass); int nonSyntheticConstructors = 0; for (Constructor candidate : rawCandidates) { if (!hetic()) { nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue; } //查找当前构造器上的注解 AnnotationAttributes ann = findAutowiredAnnotation(candidate); //若没有注解 if (ann == null) { Class userClass = rClass(beanClass); if (userClass != beanClass) { try { Constructor superCtor = laredConstructor(ameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) { // Simply proceed, no equivalent superclass } } } //若有注解 if (ann != null) { //已经存在⼀个required=true的构造器了,抛出异常 if (requiredConstructor != null) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: " + candidate + ". Found constructor with 'required' Autowired annotation already: " + requiredConstructor); } //判断此注解上的required属性 boolean required = determineRequiredStatus(ann); //若为true if (required) { if (!y()) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: " + candidates + ". Found constructor with 'required' Autowired annotation: " + candidate); } //将当前构造器加⼊requiredConstructor集合 requiredConstructor = candidate; } //加⼊适⽤的构造器集合中 (candidate); } //如果该构造函数上没有注解,再判断构造函数上的参数个数是否为0 else if (ameterCount() == 0) { //如果没有参数,加⼊defaultConstructor集合 defaultConstructor = candidate; defaultConstructor = candidate; } } //适⽤的构造器集合若不为空 if (!y()) { // Add default constructor to list of optional constructors, as fallback. //若没有required=true的构造器 if (requiredConstructor == null) { if (defaultConstructor != null) { //将defaultConstructor集合的构造器加⼊适⽤构造器集合 (defaultConstructor); } else if (() == 1 && Enabled()) { ("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: " + (0)); } } //将适⽤构造器集合赋值给将要返回的构造器集合 candidateConstructors = y(new Constructor[0]); } //如果适⽤的构造器集合为空,且Bean只有⼀个构造器并且此构造器参数数量⼤于0 else if ( == 1 && rawCandidates[0].getParameterCount() > 0) { //就使⽤此构造器来初始化 candidateConstructors = new Constructor[] {rawCandidates[0]}; } //如果构造器有两个,且默认构造器不为空 else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null && !(defaultConstructor)) { //使⽤默认构造器返回 candidateConstructors = new Constructor[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { candidateConstructors = new Constructor[] {primaryConstructor}; } else { //上述都不符合的话,只能返回⼀个空集合了 candidateConstructors = new Constructor[0]; } //放⼊缓存,⽅便下⼀次调⽤ (beanClass, candidateConstructors); } } } //返回candidateConstructors集合,若为空集合返回null return ( > 0 ? candidateConstructors : null);}从这段核⼼代码我们可以看出⼏个要点:在没有@Autowired注解的情况下:⽆参构造器将直接加⼊defaultConstructor集合中。在构造器数量只有⼀个且有参数时,此唯⼀有参构造器将加⼊candidateConstructors集合中。在构造器数量有两个的时候,并且存在⽆参构造器,将defaultConstructor(第⼀条的⽆参构造器)放⼊candidateConstructors集合中。在构造器数量⼤于两个,并且存在⽆参构造器的情况下,将返回⼀个空的candidateConstructors集合,也就是没有找到构造器。在有@Autowired注解的情况下:判断required属性:true:先判断requiredConstructor集合是否为空,若不为空则代表之前已经有⼀个required=true的构造器了,两个true将抛出异常,再判断candidates集合是否为空,若不为空则表⽰之前已经有⼀个打了注解的构造器,此时required⼜是true,抛出异常。若两者都不为空将放⼊requiredConstructor集合中,再放⼊candidates集合中。false:直接放⼊candidates集合中。判断requiredConstructor集合是否为空(是否存在required=true的构造器),若没有,将默认构造器也放⼊candidates集合中。最后将上述candidates赋值给最终返回的candidateConstructors集合。4.总结综上所述,我们可以回答开篇疑问点⼩结所总结的⼀系列问题了:为什么写三个构造器(含有⽆参构造器),并且没有@Autowired注解,Spring总是使⽤⽆参构造器实例化Bean?答:参照没有注解的处理⽅式: 若构造器只有两个,且存在⽆参构造器,将直接使⽤⽆参构造器初始化。若⼤于两个构造器,将返回⼀个空集合,也就是没有找到合适的构造器,那么参照第三节初始化Bean的第⼀段代码createBeanInstance⽅法的末尾,将会使⽤⽆参构造器进⾏实例化。这也就解答了为什么没有注解,Spring总是会使⽤⽆参的构造器进⾏实例化Bean,并且此时若没有⽆参构造器会抛出异常,实例化Bean失败。为什么注释掉两个构造器,留下⼀个有参构造器,并且没有@Autowired注解,Spring将会使⽤构造器注⼊Bean的⽅式初始化Bean?答:参照没有注解的处理⽅式: 构造器只有⼀个且有参数时,将会把此构造器作为适⽤的构造器返回出去,使⽤此构造器进⾏实例化,参数⾃然会从IOC中获取Bean进⾏注⼊。为什么写三个构造器,并且在其中⼀个构造器上打上@Autowired注解,就可以正常注⼊构造器?答:参照有注解的处理⽅式: 在最后判断candidates适⽤的构造器集合是否为空时,若有注解,此集合当然不为空,且required=true,也不会将默认构造器集合defaultConstructor加⼊candidates集合中,最终返回的是candidates集合的数据,也就是这唯⼀⼀个打了注解的构造器,所以最终使⽤此打了注解的构造器进⾏实例化。两个@Autowired注解就会报错,⼀定需要在所有@Autowired中的required都加上false即可正常初始化?答:参照有注解的处理⽅式: 当打了两个@Autowired注解,也就是两个required都为true,将会抛出异常,若是⼀个为true,⼀个为false,也将会抛出异常,⽆论顺序,因为有两层的判断,⼀个是requiredConstructor集合是否为空的判断,⼀个是candidates集合为空的判断,若两个构造器的required属性都为false,不会进⾏上述判断,直接放⼊candidates集合中,并且在下⾯的判断中会将defaultConstructor加⼊到candidates集合中,也就是candidates集合有三个构造器,作为结果返回。⾄于第四条结论,返回的构造器若有三个,Spring将如何判断使⽤哪⼀个构造器呢?在后⾯Spring会遍历三个构造器,依次判断参数是否是Spring的Bean(是否被IOC容器管理),若参数不是Bean,将跳过判断下⼀个构造器,也就是说,例如上述两个参数的构造器其中⼀个参数不是Bean,将判断⼀个参数的构造器,若此参数是Bean,使⽤⼀个参数的构造器实例化,若此参数不是Bean,将使⽤⽆参构造器实例化。也就是说,若使⽤@Autowired注解进⾏构造器注⼊,required属性都设置为false的话,将避免⽆Bean注⼊的异常,使⽤⽆参构造器正常实例化。若两个参数都是Bean,则就直接使⽤两个参数的构造器进⾏实例化并获取对应Bean注⼊构造器。在这⾥最后说⼀点,从上⾯可以看出,若想使⽤构造器注⼊功能,最好将要注⼊的构造器都打上@Autowired注解(若有多个需要注⼊的构造器,将所有@Autowired中required属性都设置为false),若有多个构造器,只有⼀个构造器需要注⼊,将这个构造器打上@Autowired注解即可,不⽤设置required属性。如果不打注解也是可以使⽤构造器注⼊功能的,但构造器数量只能为1,且代码可读性较差,读代码的⼈并不知道你这⾥使⽤了构造器注⼊的⽅式,所以这⾥我建议若使⽤构造器注⼊打上@Autowired注解会⽐较好⼀点。声明:本⽂来源于⽹络。最后如果你对技术提升很感兴趣,可以加⼊Java进阶之路来交流学习:878249276,⾥⾯都是同⾏,有资源分享包括但不限于(分布式架构、⾼可扩展、⾼性能、⾼并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。欢迎⼀到五年的⼯程师加⼊,合理利⽤⾃⼰每⼀分每⼀秒的时间来学习提升⾃⼰,不要再⽤"没有时间“来掩饰⾃⼰思想上的懒惰!趁年轻,使劲拼,给未来的⾃⼰⼀个交代!