spring boot依赖注入首选构造器注入,禁用字段注入,慎用setter注入;构造器注入保证依赖不为空、不可变且易测试,字段注入隐藏依赖并阻碍测试,setter注入仅适用于可选依赖,循环依赖时应使用@lazy而非全局开关。

Spring Boot 中依赖注入的最佳实践,核心是优先用构造器注入、禁用字段注入、慎用 setter 注入,同时配合 Lombok 和 @Lazy 解决常见痛点。不是写法越短越好,而是让依赖关系清晰、可测、不可变、启动稳定。
构造器注入:默认首选,搭配 final 和 Lombok
这是 Spring 官方推荐、社区共识最强的方式。它天然保证依赖不为空、不可修改,也便于单元测试时手动传参。
- 声明字段为 private final,Spring 会在构造时完成赋值
- 单构造函数下,
@Autowired可省略(Spring 4.3+) - 用
@RequiredArgsConstructor(Lombok)自动生成构造器,代码更干净 - 多个同类型 Bean 时,用
@Qualifier明确指定,避免歧义
示例:
@Service
@RequiredArgsConstructor
public class OrderService {
private final PaymentService paymentService;
private final @Qualifier("mainInventory") InventoryService inventoryService;
}
字段注入:明确禁止在生产环境使用
虽然写起来最省事,但它是反模式——隐藏依赖、无法单元测试、易引发 NPE、违反不可变原则。
- 字段注入无法在对象创建后验证依赖是否到位
- 测试时必须靠反射或 @MockBean,丧失轻量级测试能力
- IDE 和静态分析工具难以识别隐式依赖,重构风险高
反例(不要写):
@Service
public class UserService {
@Autowired // ❌ 不要这样用
private UserRepository userRepository;
}
Setter 注入:仅用于可选依赖或特殊场景
适用于某些 Bean 在运行时才决定是否启用(如开关控制的监控上报),或历史遗留系统中难以重构的类。
- 必须配合
@Autowired(required = false)显式表达“可选”语义 - 注意线程安全:setter 可能被多次调用,需加同步或判空保护
- 不适用于核心业务逻辑类,会削弱设计约束力
示例(谨慎使用):
@Component
public class NotificationService {
private EmailSender emailSender;
@Autowired(required = false)
public void setEmailSender(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
应对循环依赖:用 @Lazy 打断构造链,而非放开全局开关
当两个 Service 构造器互相依赖导致启动失败,spring.main.allow-circular-references=true 是临时止痛药,@Lazy 才是手术刀。
- 在构造参数上加
@Lazy,Spring 会注入一个代理对象,首次调用方法时才真正初始化目标 Bean - 它不改变 Bean 生命周期,也不引入未初始化状态风险
- 比 setter 或字段注入的循环兜底更可控、更符合设计意图
示例:
@Service
public class AService {
private final BService bService;
public AService(@Lazy BService bService) { // ✅ 延迟解析,打破循环
this.bService = bService;
}
}











