Spring的4种注入方式,你使用对了吗?

你好,我是猿java。

作为 Java程序员都知道,没有依赖注入,Spring 框架就是无法实现的,那么,在 Spring 框架中,常见的依赖注入方式有哪些呢?我们该如何选择?这篇文章来聊一聊。

从整体上来看,Spring 的依赖注入有 4种方式:

  1. 构造器注入
  2. setter 方法注入
  3. 字段注入
  4. 接口注入

下面,我们将分别分析它们的原理,以及它们的优缺点。

1. 构造器注入

构造器注入(Constructor Injection)是指通过构造函数传入依赖的对象。Spring 容器在创建 bean 时会调用它的构造函数,并将所需的依赖项作为参数传入。

如下示例展示如何通过构造器注入 bean。

1
2
3
4
5
6
public class Service {
private final Repository repository;
public MyService(Repository repository) {
this.repository = repository;
}
}

优点:

  • 强制性依赖性:在对象创建时,所有必要的依赖项都必须提供,减少了在运行时出现空指针的风险。
  • 不可变性:被注入的依赖可以声明为 final,使得一旦初始化后对象的状态不可更改,从而增强了对象的安全性。
  • 便于单元测试:构造器参数很容易模拟(mock)或替代,方便测试。

缺点:

  • 复杂性:如果一个类有很多依赖,则构造函数可能变得非常复杂,导致可读性差。
  • 潜在的构造函数过载:当添加新的依赖时,可能需要重载多个构造函数,增加了维护成本。

构造器注入是工作中比较推荐的一种方式,因为是编译期行为,所以它可以减少空指针。但是,如果一个类需要很多依赖,构造器注入会导致代码比较臃肿。

2. Setter 注入

Setter 注入(Setter Injection)是指通过 setter 方法注入依赖的对象。Spring 在创建 bean 后,通过调用 setter 方法来设置依赖项。

如下示例展示如何通过 Setter注入 bean。

1
2
3
4
5
6
7
public class Service {
private Repository repository;

public void setMyRepository(Repository repository) {
this.repository = repository;
}
}

优点:

  • 灵活性:可以选择性地注入依赖项,允许在对象创建后进行注入,适合可选的依赖。
  • 清晰的配置:可以通过 setter 方法明确地配置并查看依赖关系。

缺点:

  • 非强制性依赖:在对象创建后如果没有设置必需的依赖,可能导致运行时的空指针异常。
  • 可变性:依赖可以在对象生命周期内被更改,可能导致不一致的状态。

3. 接口注入

接口注入(Interface Injection)是一个较少使用的方式,通过一个接口将依赖注入到类中。通常实现这个接口的类会提供注入的具体实现。

如下示例展示如何通过接口注入 bean。

1
2
3
public interface DependencyInjector {
void inject(Service service);
}

优点:

  • 灵活性:可以根据不同的实现提供不同的注入方式。
  • 清晰性:接口明确了所需要的依赖,增强了代码的可理解性。

缺点:

  • 实现复杂性:需要定义额外的接口,增加了复杂性和维护成本。
  • 使用频率低:由于实现复杂,通常不被广泛采用。

4. 字段注入

字段注入(Field Injection)是直接将依赖注入到类的字段中,通常使用反射和注解。通常会 使用 Spring 提供的注解(如 @Autowired, @Inject, @Resource)。这种方式较为简洁,但是会有 NPE的风险。

如下示例展示如何通过字段注入 bean。

1
2
3
4
public class Service {
@Autowired
private Repository repository;
}

优点:

  • 简洁性:代码量较少,使用反射和注解可自动完成依赖注入,易于理解。
  • 方便快捷:无需编写构造函数或 setter 方法。

缺点:

  • 不易于测试:由于字段是私有的,通常不容易替换依赖项进行测试。
  • 依赖性不明显:依赖关系并不明确,影响代码的可读性和维护性。
  • 不支持不可变性:不能将字段声明为 final,这可能导致不一致的状态。

5. 总结

本文,我们分析了 Spring的 4种注入方式,在选择依赖注入方式时,我们应该根据具体情况和项目来决定。根据工作经验,建议如下:

  • 构造器注入是最推荐的方式,特别是在需要强制性依赖和不可变性的场景下。
  • 如果无法通过构造器注入,再选择 setter注入。
  • 如果上述两者方式都不适用,字段注入则是最后的选择,虽然使用简单,但是容易产生NPE。

6. 学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

drawing