spring为何推荐使用构造器注入

1、前言

​  项目中遇到一个问题:项目启动完成前,在A类中注入B类,并调用B类的某个方法。html

  那么调用B类的这个方法写在哪里呢,我选择写到构造器里,可是构造器先于Spring注入执行,那么执行构造器时,注入B类确定为null,因而选择了构造器注入,解决问题java

 

  执行顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>Spring注入spring

 

2、常见的三种注入方式

​ 笔者为了方便起见就只是用注解的方式注入(如今也不多使用xml了吧)app

2.1 注解注入

@Controller
public class FooController {
  @Autowired
  private FooService fooService;
  
  //简单的使用例子,下同
  public List<Foo> listFoo() {
      return fooService.list();
  }
}

  

这种注入方式应该是笔者目前为止开发中见到的最多见的注入方式。缘由很简单:less

  1. 注入方式很是简单:加入要注入的字段,附上@Autowired,便可完成。
  2. 使得总体代码简洁明了,看起来美观大方。

2.2 构造器注入

@Controller
public class FooController {
  
  private final FooService fooService;
  
  @Autowired
  public FooController(FooService fooService) {
      this.fooService = fooService;
  }
  
  //使用方式上同,略
}

  

​ 在Spring4.x版本中推荐的注入方式就是这种,相较于上面的注解注入方式而言,就显得有点难看,特别是当注入的依赖不少(5个以上)的时候,就会明显的发现代码显得很臃肿。对于从注解注入转过来+有强迫症的园友 来讲,简直能够说是没法忍受。对于这一点咱们后面再来讨论,别急。

函数

2.3 setter注入

@Controller
public class FooController {
  
  private FooService fooService;
  
  //使用方式上同,略
  @Autowired
  public void setFooService(FooService fooService) {
      this.fooService = fooService;
  }
}

  

​ 在Spring3.x刚推出的时候,推荐使用注入的就是这种,笔者如今也基本没看到过这种注解方式,写起来麻烦,当初推荐Spring天然也有他的道理,这里咱们引用一下Spring当时的原话:测试

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.ui

Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.this

​ 咳咳,简单的翻译一下就是:构造器注入参数太多了,显得很笨重,另外setter的方式能用让类在以后从新配置或者从新注入

spa

​ 那么后面为何又换成构造器注入了呢?(喂喂喂,Spring你前一大版本还贬低构造器注入,后面就马上捧人家了很差吧,不过能用于认可本身的错误,才是真正使人称赞的地方吧 (๑•̀ㅂ•́)و✧)

3、构造器注入的好处

​ 先来看看Spring在文档里怎么说:

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

​ 咳咳,再来简单的翻译一下:这个构造器注入的方式啊,可以保证注入的组件不可变,而且确保须要的依赖不为空。此外,构造器注入的依赖老是可以在返回客户端(组件)代码的时候保证彻底初始化的状态

下面来简单的解释一下:

  • 依赖不可变:其实说的就是final关键字,这里再也不多解释了。不明白的园友能够回去看看Java语法。
  • 依赖不为空(省去了咱们对其检查):当要实例化FooController的时候,因为本身实现了有参数的构造函数,因此不会调用默认构造函数,那么就须要Spring容器传入所须要的参数,因此就两种状况:一、有该类型的参数->传入,OK 。2:无该类型的参数->报错。因此保证不会为空,Spring总不至于传一个null进去吧 :-( 
  • 彻底初始化的状态:这个能够跟上面的依赖不为空结合起来,向构造器传参以前,要确保注入的内容不为空,那么确定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程当中,构造方法是最后一步(以前若是有父类先初始化父类,而后本身的成员变量,最后才是构造方法,这里不详细展开。)。因此返回来的都是初始化以后的状态。

等等,比较完了setter注入与构造器注入的优缺点,你还没用说使用field注入与构造器的比较呢!那么咱们再回头看一看使用最多的field注入方式:

//承接上面field注入的代码,假如客户端代码使用下面的调用(或者再Junit测试中使用) //这里只是模拟一下,正常来讲咱们只会暴露接口给客户端,不会暴露实现。 FooController fooController = new FooController(); fooController.listFoo(); // -> NullPointerException

若是使用field注入,缺点显而易见,对于IOC容器之外的环境,除了使用反射来提供它须要的依赖以外,没法复用该实现类。并且将一直是个潜在的隐患,由于你不调用将一直没法发现NPE的存在。

还值得一提另一点是:使用field注入可能会致使循环依赖,即A里面注入B,B里面又注入A:

public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A a;
}

  

若是使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,若是是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错

 

4、答疑

​ 好了,相信已经园友们知道了构造器注入的好处,那么回到了在前面提到的问题:

Q1:跟3.x里说的同样,我要是有大量的依赖要注入,构造方法不会显得很臃肿吗?

对于这个问题,说明你的类当中有太多的责任,那么你要好好想想是否是本身违反了类的单一性职责原则,从而致使有这么多的依赖要注入。

Q2:是否是其余的注入方式都不适合用了呢?

固然不是,存在便是合理!setter的方式既然一开始被Spring推荐确定是有它的道理,像以前提到的setter的方式能用让类在以后从新配置或者从新注入,就是其优势之一。除此以外,若是一个依赖有多种实现方式,咱们可使用@Qualifier,在构造方法里选择对应的名字注入,也可使用field或者setter的方式来手动配置要注入的实现。

5、总结

​ 使用构造器注入的好处:

  1. 保证依赖不可变(final关键字)
  2. 保证依赖不为空(省去了咱们对其检查)
  3. 保证返回客户端(调用)的代码的时候是彻底初始化的状态
  4. 避免了循环依赖
  5. 提高了代码的可复用性

另外,当有一个依赖有多个实现的使用,推荐使用field注入或者setter注入的方式来指定注入的类型。这是spring官方博客对setter注入方式和构造器注入的比较。谢谢各位园友观看,若是有描述不对的地方欢迎指正,与你们共同进步!

相关文章
相关标签/搜索