一、核心结论(先背这个)
Spring 推荐构造器注入,而不推荐字段注入,
是因为构造器注入能保证对象在创建完成时依赖已经全部就绪,
而字段注入会让对象在一段时间内处于"不完整状态"。
二、两种注入方式的本质区别
1️⃣ 构造器注入(Constructor Injection)
java
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
}
本质:
- 依赖作为构造器参数
- 没有依赖就无法创建对象
- 依赖在
new之前就必须准备好
创建顺序(类比"深度优先"):
text
创建 A
→ 先创建 B
→ 再创建 C
→ 再创建 A
2️⃣ 字段注入(Field Injection)
java
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
}
本质:
- 先
new一个"空壳对象" - 再通过反射把依赖"塞进去"
创建顺序:
text
new OrderService() ← 依赖此时是 null
↓
反射注入 orderRepository
三、字段注入真实会出错的原因
❌ 出错根源(不是 Spring 的问题)
对象在依赖注入完成之前就已经存在,并且可能被使用
❌ 典型错误示例
java
@Service
public class ReportService {
@Autowired
private UserRepository userRepository;
public ReportService() {
// 此时 userRepository 还没注入
userRepository.findAll(); // 💥 NPE
}
}
原因:
- 构造器执行时,字段注入尚未发生
- 依赖是
null
✅ 构造器注入不会有这个问题
java
public ReportService(UserRepository userRepository) {
this.userRepository = userRepository;
userRepository.findAll(); // 一定安全
}
四、设计层面的差异(面试加分点)
字段注入的问题
- 依赖关系 隐藏
- 无法使用
final - 不利于单元测试
- 容易掩盖设计问题(依赖过多)
构造器注入的优势
- 依赖关系 一眼可见
- 支持
final(不可变) - 创建即完整
- 更容易测试
- 循环依赖会更早暴露
五、你提出的"深度优先"理解(标准化表述)
构造器注入在 Bean 创建阶段会先解析并实例化其构造器参数,
形成一种类似 深度优先 的依赖构建过程,
从而保证对象在创建完成后依赖已经全部注入完成。
而字段注入是在对象实例化之后再进行依赖填充,
对象在此期间可能处于不完整状态。
六、30 秒面试速记版(终极版)
构造器注入是在对象创建时通过构造方法一次性注入依赖,
可以保证对象创建完成后就是可用状态;
字段注入是先创建对象再通过反射注入依赖,
对象可能短暂处于不完整状态,因此 Spring 官方更推荐构造器注入。