不同依赖注入方式有哪些及其区别
在 Spring 中,依赖注入(Dependency Injection, DI)是实现控制反转(IOC)的核心方式,主要有三种注入方式:构造器注入、Setter 注入 和 字段注入。它们通过不同途径将依赖注入到 Bean 中,区别在于注入位置、代码风格、可读性和适用场景。
1. 不同依赖注入方式
(1) 构造器注入(Constructor Injection)
- 定义:通过构造器参数注入依赖。
- 实现:
- 使用
@Autowired
或显式构造器。 - 代码:
@Component
public class UserService {
private final UserDao userDao;
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
(2) Setter 注入(Setter Injection)
- 定义:通过 Setter 方法注入依赖。
- 实现:
- 使用
@Autowired
标注 Setter。 - 代码:
@Component
public class UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
(3) 字段注入(Field Injection)
- 定义:直接在字段上使用注解注入依赖。
- 实现:
- 使用
@Autowired
或@Resource
。 - 代码:
@Component
public class UserService {
@Autowired
private UserDao userDao;
}
其他(补充)
- 接口注入(较少使用):
- 通过接口定义注入方法,Spring 不直接支持。
- XML 配置注入:
- 通过
<constructor-arg>
或<property>
配置(老式方式)。
2. 区别
维度 | 构造器注入 | Setter 注入 | 字段注入 |
---|---|---|---|
注入位置 | 构造器参数 | Setter 方法 | 字段直接 |
依赖声明 | 强制(构造时注入) | 可选(可不调用 Setter) | 可选(反射注入) |
不可变性 | 支持(final 字段) |
不支持(可多次设置) | 不支持(可反射修改) |
代码可读性 | 明确(构造器显式) | 中等(需找 Setter) | 简洁(但隐藏依赖) |
测试性 | 强(手动构造传入) | 中等(需调用 Setter) | 弱(依赖反射) |
Spring 默认 | @Autowired 可省略(唯一构造器) |
需显式标注 | 需显式标注 |
适用场景 | 核心依赖 | 可选依赖 | 简单场景(不推荐) |
详细对比
- 依赖强弱:
- 构造器:强制依赖,Bean 创建时必须注入。
- Setter:可选依赖,可运行时动态调整。
-
字段:隐式依赖,不强制。
-
不可变性:
- 构造器:支持
final
,防止修改。 -
Setter/字段:可变,存在风险。
-
代码风格:
- 构造器:符合“依赖显式声明”原则。
- Setter:传统风格,灵活但冗长。
-
字段:简洁但隐藏依赖关系。
-
异常处理:
- 构造器:依赖缺失,启动失败(早期发现)。
- Setter/字段:运行时才暴露问题。
3. 示例(综合)
@Component
public class UserService {
private final UserDao userDao; // 构造器注入
private Logger logger; // Setter 注入
@Autowired
private Config config; // 字段注入
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
@Autowired
public void setLogger(Logger logger) {
this.logger = logger;
}
}
4. 优缺点与选择
构造器注入
- 优点:
- 依赖明确,初始化即完成。
- 支持不可变对象(
final
)。 - 测试友好(手动构造)。
- 缺点:
- 构造器参数多时代码冗长。
- 场景:核心依赖,必选依赖。
Setter 注入
- 优点:
- 灵活,可选依赖动态调整。
- 支持循环依赖(Spring 允许)。
- 缺点:
- 不可变性差,运行时可变。
- 依赖不直观。
- 场景:可选依赖,配置变更。
字段注入
- 优点:
- 代码简洁,编写快。
- 缺点:
- 隐藏依赖,测试困难(需反射)。
- 不可变性差,社区不推荐。
- 场景:简单原型,快速开发(生产慎用)。
延伸与面试角度
- Spring 演进:
- 4.0+ 默认支持构造器注入(唯一构造器可省
@Autowired
)。 - 循环依赖:
- 构造器:无法解决,启动报错。
- Setter/字段:Spring 通过代理解决。
- 最佳实践:
- 优先构造器注入,Setter 辅助,尽量避免字段注入。
- 面试点:
- 问“区别”时,提不可变性和测试。
- 问“选择”时,提场景和 Spring 推荐。
总结
Spring DI 包括构造器、Setter 和字段注入,区别在注入方式、依赖性、可读性和场景。构造器注入最优,Setter 灵活,字段简洁但不推荐。面试时,可写示例或提循环依赖,展示理解深度。