在 Spring 框架中,依赖注入(Dependency Injection, DI)是一种常用的设计模式,用于管理对象之间的依赖关系。
Spring 支持多种依赖注入方式,
包括基于 setter 的注入、基于构造器的注入以及基于注解的注入。
下面分别介绍这三种方式及其区别:
1. Setter 注入 (Set-based Injection)
这是最早的依赖注入方式,在 Spring 初期非常流行。这种方式通过类的 setter 方法将依赖注入到类中。
特点:
- 可选性:依赖可以在运行时决定是否注入。
- 延迟初始化:依赖可以在任何时间点注入,甚至可以在第一次调用 setter 方法时才注入。
- 测试性:由于依赖是通过 setter 方法注入的,因此可以通过构造无依赖的对象来进行单元测试。
示例代码:
public class MyClass {
private MyDependency dependency;
public void setDependency(MyDependency dependency) {
this.dependency = dependency;
}
}
配置示例:
<bean id="myClass" class="com.example.MyClass">
<property name="dependency" ref="myDependency"/>
</bean>
<bean id="myDependency" class="com.example.MyDependency"/>
2. 构造器注入 (Constructor-based Injection)
这种方式通过构造器来注入依赖。这是 Spring 推荐的依赖注入方式之一,因为它能够确保依赖关系在创建对象时就已经被满足,并且使得对象在创建后处于一种完整可用的状态。
特点:
- 强制性:依赖必须在构造对象时提供。
- 不可变性:依赖一旦注入就无法更改。
- 更好的测试性:因为依赖是在构造器中提供的,所以可以通过不同的构造器参数来进行测试。
- 更安全:确保了依赖不会是 null。
示例代码:
public class MyClass {
private final MyDependency dependency;
public MyClass(MyDependency dependency) {
this.dependency = dependency;
}
}
配置示例:
<bean id="myClass" class="com.example.MyClass">
<constructor-arg ref="myDependency"/>
</bean>
<bean id="myDependency" class="com.example.MyDependency"/>
3. 注解注入 (Annotation-based Injection)
这种方式使用注解来指定依赖注入的位置。这是现代 Spring 应用中最常用的依赖注入方式,因为它简化了配置文件并使代码更加简洁。
特点:
- 简洁性:不需要 XML 配置文件,依赖注入可以通过注解完成。
- 灵活性:可以用于字段注入、setter 方法注入或者构造器注入。
- 易读性:依赖关系直接在类定义中清晰地表达出来。
示例代码:
public class MyClass {
@Autowired
private MyDependency dependency;
}
配置示例:
@Configuration
public class AppConfig {
@Bean
public MyClass myClass() {
return new MyClass(myDependency());
}
@Bean
public MyDependency myDependency() {
return new MyDependency();
}
}
总结
- setter 注入:比较灵活,但可能导致未初始化的对象状态。
- 构造器注入:提供了更强的保证,即对象创建时依赖已经满足,适合于必需的依赖。
- 注解注入:最现代和简洁的方式,通常与构造器注入一起使用。
Spring 官方推荐使用构造器注入,因为这种方式可以更好地支持不可变性和最终性,有助于创建易于理解和维护的代码。同时,注解注入(尤其是字段注入)也经常被使用,因为它简化了代码。但在使用字段注入时需要注意避免潜在的副作用,比如依赖的不确定性。