Spring的注入方式有哪些?该如何选择?

Spring的注入方式有哪些?该如何选择?

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

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

构造器注入

setter 方法注入

字段注入

接口注入

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

1. 构造器注入

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

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

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。

public class Service {

private Repository repository;

public void setMyRepository(Repository repository) {

this.repository = repository;

}

}

优点:

灵活性:可以选择性地注入依赖项,允许在对象创建后进行注入,适合可选的依赖。

清晰的配置:可以通过 setter 方法明确地配置并查看依赖关系。

缺点:

非强制性依赖:在对象创建后如果没有设置必需的依赖,可能导致运行时的空指针异常。

可变性:依赖可以在对象生命周期内被更改,可能导致不一致的状态。

3. 接口注入

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

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

public interface DependencyInjector {

void inject(Service service);

}

优点:

灵活性:可以根据不同的实现提供不同的注入方式。

清晰性:接口明确了所需要的依赖,增强了代码的可理解性。

缺点:

实现复杂性:需要定义额外的接口,增加了复杂性和维护成本。

使用频率低:由于实现复杂,通常不被广泛采用。

4. 字段注入

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

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

public class Service {

@Autowired

private Repository repository;

}

优点:

简洁性:代码量较少,使用反射和注解可自动完成依赖注入,易于理解。

方便快捷:无需编写构造函数或 setter 方法。

缺点:

不易于测试:由于字段是私有的,通常不容易替换依赖项进行测试。

依赖性不明显:依赖关系并不明确,影响代码的可读性和维护性。

不支持不可变性:不能将字段声明为 final,这可能导致不一致的状态。

5. 总结

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

构造器注入是最推荐的方式,特别是在需要强制性依赖和不可变性的场景下。

如果无法通过构造器注入,再选择 setter注入。

如果上述两者方式都不适用,字段注入则是最后的选择,虽然使用简单,但是容易产生NPE。