依赖注入DI的三种注入方式

依赖注入DI的三种注入方式

1. 什么是依赖注入,DI的作用是什么?

依赖注入DI的作用是将对象之间的依赖关系从代码中解耦出来,由容器统一负责对象的创建、装配和生命周期管理。你可以通过构造函数或字段注入的方式来告诉Spring需要哪些对象,Spring会负责为你提供。

DI的本质:把依赖对象的创建和管理交给 Spring 容器,类只需要专注于自己的业务逻辑,不需要操心依赖从哪里来。

2. DI的三种注入方式

  1. 构造方法注入【最推荐】
  2. Setter 注入
  3. 字段注入【最不推荐】

2.1 构造方法注入(Constructor Injection)【★★★★★ 推荐】

写法示例

1
2
3
4
5
6
7
8
9
@Service
public class OrderService {

private final UserService userService;

public OrderService(UserService userService) {
this.userService = userService;
}
}

使用Lombok的@RequiredArgsConstructor注解可以简化成:

1
2
3
4
5
6
@Service
@RequiredArgsConstructor
public class OrderService {

private final UserService userService;
}

2.2 Setter方法注入(Setter Injection)

写法示例

1
2
3
4
5
6
7
8
9
10
@Service
public class OrderService {

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}

在Setter方法上添加@Autowired注解实现动态注入

2.3 字段注入(Field Injection)【★ 最不推荐】

写法示例

1
2
3
4
5
6
7
8
9
10
@Service
public class OrderService {

@Autowired
private UserService userService;

// 或者:
@Resource
private UserService userService;
}

直接在字段上注入

3. 三种注入方式的对比

更推荐使用:
构造器注入 > Setter 注入 > 字段注入

3.1 原因1:依赖是否“强制完整”

依赖是否在对象创建时就完整是第一优先级 → 构造器 > Setter > 字段

  • 构造器注入在对象创建时必须传入所有依赖,否则对象无法被实例化
  • Setter注入对象可以先被创建,再通过Setter方法注入依赖,可能存在“对象已被使用但依赖尚未注入”的风险
  • 字段注入对象创建时依赖并不明确,完全依赖 Spring 容器通过反射在运行期赋值,一旦容器注入失败,容易在运行期产生NullPointerException

3.2 是否支持“不可变对象 & 线程安全”

  • 构造器注入(支持 final,天然线程安全),是不可变对象
  • Setter 注入(❌ 运行期可被修改)
  • 字段注入(❌ 无法使用 final)(Spring 通过反射注入)

3.3 是否有利于“单元测试 & Mock”

测试友好度:构造器 >> Setter > 字段

  • 构造器注入(✅ 最有利于单测)
  • Setter 注入(⚠️ 勉强可测)
  • 字段注入(❌ 几乎无法做“纯单测”)
对比维度 构造器注入 Setter 注入 字段注入
推荐级别 ✅✅✅ 强烈推荐 ⚠️ 一般推荐 ❌ 明确不推荐
依赖是否强制 ✅ 强制 ❌ 可选 ❌ 不可控
依赖是否显式 ✅ 构造器参数可见 ⚠️ 部分可见 ❌ 完全隐藏
是否支持 final ✅ 支持 ❌ 不支持 ❌ 不支持
单元测试友好性 ✅ 最好 ⚠️ 一般 ❌ 最差
线程安全 ✅ 高 ❌ 低 ❌ 最低
是否依赖反射 ❌ 不依赖 ❌ 不依赖 ✅ 强依赖
企业级项目适用性 ✅ 默认标准 ⚠️ 仅限可选依赖 ❌ 禁用

END