为什么要使用构造方法进行依赖注入?

一、前言

我们在使用Spring框架进行开发时,不可避免的要进行依赖注入(Dependency Injection),也就是把实例从Spring容器中取出来进行使用。Spring的依赖注入方式主要有三种,分别为Constructor、Setter和Field。有了选择的余地,令人纠结的地方就来了,这三种方式哪个更好一些呢?

二、注入方式对比

Constructor注入

private DependencyA dependencyA;
@Autowired
public DI(DependencyA dependencyA) {
    this.dependencyA = dependencyA;
}

Setter注入

private DependencyB dependencyB;
@Autowired
public void setDependencyB(DependencyB dependencyB) {
    this.dependencyB = dependencyB;
}

Field注入

@Autowired
private DependencyC dependencyC;

三、选择哪种注入方式

对比这三种方式,Field注入显得清爽又整洁。写的时候方便快捷,读起来也很赏心悦目,看起来肯定要优先选择使用它啊。然而,这种方式是存在一些缺点的。

  • 由于操作简单,代码简洁,使用者可能稍不留神注入了太多的依赖,造成该类承担了太多的责任。违背了单一职责的设计原则。至于为什么要遵从单一原则,可自行百度。
  • 由于没有提供全参构造或set方法,那么使用者有可能通过默认的空参构造new出实例,使用时发生空指针异常。
  • 此类不能在DI容器(测试,其他模块)之外重用,因为除了反射以外,没有其他方法可以为其提供所需的依赖关系。
  • 有可能产生循环依赖报出BeanCurrentlyInCreationException,下方只是一个例子,现实中出现的情况会更难缠。
public class A {
    @Autowired
    private B b;
}
public class B {
    @Autowired
    private C c;
}
public class C {
    @Autowired
    private A a;
}

Spring官方目前推荐的是构造器注入。根据官方的说法,因为它使人们能够将应用程序组件实现为不可变对象,并确保所需的依赖项不为null。此外,注入构造函数的组件总是以完全初始化的状态返回到客户端(调用)代码。

  • 不可变对象:说的是可以是字段用final关键字修饰。

  • 依赖不为null:因为有了自定义的构造函数,所以程序不再提供默认的空参构造,类在实例化时必须传入所有需要的参数。

  • 完全初始化的状态:构造方法的作用就是初始化成员变量,在Java类加载实例化的过程中,构造方法是最后一步,所以返回来的组件都是初始化之后的状态。

总结

Field注入

优点

  • 最简洁

缺点

  • 便利会弱化代码结构设计

  • 很难测试

  • 依赖不能是可变的(无法final)

  • 容易出现循环依赖

  • 需要使用到多个spring或者java注解

Set注入

优点

  • 对循环依赖免疫
  • 随着setter的添加,高度耦合的类很容易被识别出来。

缺点

  • 违反开放封闭原则
  • 会把循环依赖隐藏掉
  • 三种方法里最模板化的方式
  • 依赖不能是可变的(无法final)

Constructor注入

优点

  • 依赖可以是final的

  • spring官方推荐的方式

  • 三种方式里最容易测试的方式

  • 高耦合类随着构造参数的增长很容易被识别出来

  • 其他开发平台的开发者也很熟悉

  • 不需要依赖@Autowired注解

缺点

  • 构造函数需要下沉到子类

总的来说,三种方式的优劣不是一个“或非”选择,开发者可以将setter和构造函数注入结合在一个类中。构造函数更适合于强制性依赖关系以及针对不变性的情况,设置器更适合于可选的依赖项。

猜你喜欢

转载自www.cnblogs.com/hanstrovsky/p/11601010.html