构造器注入是推荐的首选方式,因其能保证依赖不可变且强制非空,支持final字段、测试友好、暴露循环依赖问题;setter注入适用于可选或可重配置依赖;字段注入应避免,因破坏封装、难测试且强耦合容器。

构造器注入为什么是推荐的首选方式
Spring 官方文档明确建议优先使用构造器注入,核心原因是它能保证依赖不可变且强制非空。一旦 Bean 初始化完成,final 字段就无法被修改,避免了后续被意外覆盖或设为 null 的风险。
- 构造器注入天然支持不可变性:所有依赖在对象创建时一次性传入,适合有状态、无副作用的组件
- Spring 5.0+ 对构造器注入做了优化,即使不写
@Autowired(单构造器场景)也能自动装配 - 测试友好:可直接 new 实例并传入 mock 依赖,无需启动 Spring 上下文
- 循环依赖会直接失败(抛出
BeanCurrentlyInCreationException),这反而是好事——暴露设计问题,而不是掩盖成运行时 NPE
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
// Spring 5.2+ 单构造器可省略 @Autowired
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
}
Setter 注入只适用于可选或可重配置的依赖
Setter 注入灵活性高,但破坏了对象的完整性约束。它适用于那些可能为 null、需要运行时重设、或属于回调扩展点的依赖(比如监听器、策略实现)。
- 必须配合
@Autowired(required = false)显式声明可选性,否则 Spring 会报NoSuchBeanDefinitionException - 不能用于
final字段,也无法阻止后续被多次调用 setter 覆盖 - 在 AOP 代理场景下(如
@Transactional),setter 方法可能被代理拦截,导致意料外的行为 - 与 Lombok 的
@RequiredArgsConstructor混用时容易冲突,Lombok 生成的构造器不会包含@Autowired标记的 setter 依赖
public class NotificationService {
private EmailService emailService;
@Autowired(required = false)
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
}
字段注入(@Autowired 直接写在属性上)是应该避免的反模式
字段注入看似简洁,但会带来测试困难、违反封装、无法构建不可变对象等问题。Spring 团队早在 2016 年就公开指出它是“bad practice”,并在后续版本中持续弱化其地位。
- 字段注入使类强耦合 Spring 容器:无法脱离 IoC 环境单独实例化,单元测试必须用
@ContextConfiguration或反射赋值 - 无法标记字段为
final,失去编译期空安全校验 - IDE 和静态分析工具(如 SonarQube)会警告
spring-beans:field-injection - 在 Spring Boot 3.x + Jakarta EE 9+ 环境中,若未启用
spring.main.allow-circular-references=true,字段注入更容易触发循环依赖死锁
public class OrderService {
// ❌ 不推荐
@Autowired
private PaymentGateway paymentGateway;
}
@Resource 和 @Inject 能替代 @Autowired 吗
可以语法上使用,但语义和行为不同,混用容易引发装配失败或隐式行为。
立即学习“Java免费学习笔记(深入)”;
-
@Autowired是 Spring 原生注解,按类型(byType)匹配,配合@Qualifier指定名称 -
@Resource是 JSR-250 标准注解,默认按名称(byName)匹配,name属性不指定时 fallback 到字段名;不支持@Qualifier -
@Inject是 JSR-330 标准注解,行为类似@Autowired(byType),但不支持required = false,必须配@Nullable实现可选注入 - 当同时存在多个同类型 Bean 时,仅靠
@Resource(name = "xxx")可能绕过 Spring 的类型检查逻辑,导致运行时类型转换异常
实际项目中,统一用 @Autowired + @Qualifier 最可控;引入第三方库强制要求 JSR-330 时再用 @Inject,但务必确认其是否兼容 Spring 的泛型擦除处理逻辑。










