依赖注入DI的三种注入方式
依赖注入DI的三种注入方式
1. 什么是依赖注入,DI的作用是什么?
依赖注入DI的作用是将对象之间的依赖关系从代码中解耦出来,由容器统一负责对象的创建、装配和生命周期管理。你可以通过构造函数或字段注入的方式来告诉Spring需要哪些对象,Spring会负责为你提供。
DI的本质:把依赖对象的创建和管理交给 Spring 容器,类只需要专注于自己的业务逻辑,不需要操心依赖从哪里来。
2. DI的三种注入方式
- 构造方法注入【最推荐】
- Setter 注入
- 字段注入【最不推荐】
2.1 构造方法注入(Constructor Injection)【★★★★★ 推荐】
写法示例
1 | |
使用Lombok的@RequiredArgsConstructor注解可以简化成:
1 | |
2.2 Setter方法注入(Setter Injection)
写法示例
1 | |
在Setter方法上添加@Autowired注解实现动态注入
2.3 字段注入(Field Injection)【★ 最不推荐】
写法示例
1 | |
直接在字段上注入
3. 三种注入方式的对比
更推荐使用:
构造器注入 > Setter 注入 > 字段注入
3.1 原因1:依赖是否“强制完整”
依赖是否在对象创建时就完整是第一优先级 → 构造器 > Setter > 字段
- 构造器注入在对象创建时必须传入所有依赖,否则对象无法被实例化
- Setter注入的对象可以先被创建,再通过Setter方法注入依赖,可能存在“对象已被使用但依赖尚未注入”的风险
- 字段注入:对象创建时依赖并不明确,完全依赖 Spring 容器通过反射在运行期赋值,一旦容器注入失败,容易在运行期产生NullPointerException
3.2 是否支持“不可变对象 & 线程安全”
- 构造器注入(支持 final,天然线程安全),是不可变对象
- Setter 注入(❌ 运行期可被修改)
- 字段注入(❌ 无法使用 final)(Spring 通过反射注入)
3.3 是否有利于“单元测试 & Mock”
测试友好度:构造器 >> Setter > 字段
- 构造器注入(✅ 最有利于单测)
- Setter 注入(⚠️ 勉强可测)
- 字段注入(❌ 几乎无法做“纯单测”)
| 对比维度 | 构造器注入 | Setter 注入 | 字段注入 |
|---|---|---|---|
| 推荐级别 | ✅✅✅ 强烈推荐 | ⚠️ 一般推荐 | ❌ 明确不推荐 |
| 依赖是否强制 | ✅ 强制 | ❌ 可选 | ❌ 不可控 |
| 依赖是否显式 | ✅ 构造器参数可见 | ⚠️ 部分可见 | ❌ 完全隐藏 |
是否支持 final |
✅ 支持 | ❌ 不支持 | ❌ 不支持 |
| 单元测试友好性 | ✅ 最好 | ⚠️ 一般 | ❌ 最差 |
| 线程安全 | ✅ 高 | ❌ 低 | ❌ 最低 |
| 是否依赖反射 | ❌ 不依赖 | ❌ 不依赖 | ✅ 强依赖 |
| 企业级项目适用性 | ✅ 默认标准 | ⚠️ 仅限可选依赖 | ❌ 禁用 |