Circulardependency这个问题,按理说我们应该在日常编程中避免,其实这个也是可以避免的。但是,由于一般的原因,我们还是会遇到一些循环依赖的问题,尤其是在面试的过程中。面试考察循环依赖,主要考察考生对Spring源码的熟悉程度,因为需要把问题解释清楚,而且涉及到大量的Spring源码。宋兄今天抽空跟大家简单聊了聊这个话题。问题相当大。我可能会花几篇文章分享给大家。今天先说说实例的注入方式。1.实例注入方式首先我们来看一下Spring是如何注入实例的。总结起来无外乎三种:属性注入、set方法注入、构造方法注入。让我们分别看看它们。1.1属性注入属性注入是最常见也是使用最多的注入方式。代码如下:@ServicepublicclassBService{@AutowiredAServiceaService;//...}这里是使用@Autowired注解进行注入。另外还有@Resource、@Inject等注解,可以实现注入。但是不知道小伙伴们有没有注意到,在IDEA中,使用属性注入的时候,会出现警告??:不推荐属性注入!原因将在后面讨论。1.2set方法注入set方法注入过于臃肿,实际上很少用到:@ServicepublicclassBService{AServiceaService;@AutowiredpublicvoidsetaService(AServiceaService){this.aService=aService;}}这段代码乍一看感觉不太舒服,所以坚决不使用。1.3构造函数注入构造函数注入方法如下:@ServicepublicclassAService{BServicebService;@AutowiredpublicAService(BServicebService){this.bService=bService;}}如果类只有一个构造函数,@Autowired注解可以省略;如果类方法中有多个构造函数,则需要添加@Autowired明确指定使用哪个构造函数。2.实例注入方式大PK。上面列举了三种注入方式,那么三种注入方式有哪些区别呢?下面结合Spring官方文档来分析一下。宋哥翻出12年前的Spring3.0文档(https://docs.spring.io/spring-framework/docs/3.0.x/reference/beans.html),里面有这么一段话:我将简单翻译(意译):使用构造函数注入还是set方法注入?由于构造函数注入和set方法注入可以混合使用,如果需要强制注入,我们可以使用构造函数注入;如果是可选注入,那么我们可以使用set方法注入方式。当然,我们在setter上使用@Required注解,强制set方法注入。Spring团队通常提倡setter注入,因为当属性过多时,构造器会显得特别臃肿,尤其是当属性是optional的时候(propertyoptional意味着不需要通过构造器注入)。setter方法注入的另一个好处是它使类的属性能够在以后重新配置或重新注入。一些纯粹主义者喜欢基于构造函数的注入,这意味着所有属性都被初始化,缺点是对象变得不太适合重新配置和重新注入。另外,在一些特殊的场景下,比如一个第三方类要注入到Spring容器中,但是这个类没有提供set方法,那么这时候就只能使用构造函数注入了。英文水平有限,大概翻译一下。小伙伴们,重点看加粗的部分,也就是说在Spring3.0时代,官方还是提倡set方法注入。但是从Spring4.x开始,官方不再推荐这种注入方式,转而推荐使用构造函数注入。让我们看看Spring4.x文档(https://docs.spring.io/spring-framework/docs/4.0.x/spring-framework-reference/htmlsingle/#beans-setter-injection)是怎么说的:这一段内容我就不一一翻译了。你要重点看第二段第一句:Spring团队普遍提倡构造函数注入这句话的意思是Spring团队提倡通过构造方法注入。只是大版本更新而已,为什么Spring变了?别着急,人家也给出了用构造方法注入的理由。第二段的翻译大概是这个意思:通过构造方法的注入,可以保证注入的组件不可用。更改,并能保证需要的依赖不为空。此外,当返回到客户端(组件)代码时,构造函数注入的依赖项始终保证处于完全初始化状态。上面这段话主要讲了三件事:Dependencyisimmutable:这个很容易理解。依赖是通过构造方法注入的,必须在创建对象的时候注入依赖。一旦对象创建成功,注入的依赖只能在以后使用。修改了,这个依赖于不变性(通过set方法注入,以后也可以通过set方法修改)。依赖不为空:通过构造函数注入时,会自动检查注入的对象是否为空。如果为空,则注入失败;如果不为空,则注入成功。完全初始化:由于获得了依赖对象(这个依赖对象被初始化),并且调用了待初始化组件的构造函数,所以最终得到了完全初始化的对象。在Spring3.0的文档中,官方说如果使用构造函数注入,过多的属性可能会使代码非常臃肿,所以在4.0的文档中,官方也对这种说法做了一些修正:如果使用构造函数注入,参数太多导致代码过于臃肿,那么就需要考虑这个类的设计是否合理,这个类是不是混杂了太多其他不相关的功能,这个类是否做到了职责单一。嗯,你说的总是有道理的!这是构造函数注入和set方法注入的问题。那么我们上面提到了不推荐属性注入。这是怎么回事?属性注入实际上有一个明显的缺点。即对于IOC容器以外的环境,除了使用反射来提供它所需要的依赖外,其实现类是不能被复用的。因为这个类没有提供属性的set方法或者相应的构造方法来完成属性的初始化。也就是说,如果你使用属性注入,你的类只能在IOC容器中使用。如果你想自己new这个类的对象,那么相关的依赖是无法注入的。以上分析基于Spring官方文档。日常开发中应该多一些属性注入。我们不必为此担心。代码怎么写就是怎么写。Spring官方的态度可以理解。当然,如果项目允许的话,不妨试试Spring推荐的编码约定。3.总结好了,今天就和小伙伴们聊一聊Spring中的注入方式,因为最近又要接Spring源码分析,先来个简单的热身吧哈哈~
