spring依赖注入(di)通过容器管理对象依赖实现解耦,推荐构造器注入保障不可变性与必需依赖强制提供,setter注入适用于可选依赖,字段注入不推荐用于业务逻辑,配合接口编程支持实现类灵活替换。

Spring 通过依赖注入(DI)把对象之间的依赖关系从代码里“拿出去”,交给容器统一管理,从而实现组件解耦。核心不是你去 new 一个依赖,而是告诉 Spring “我需要什么”,它自动送过来——类之间不再硬编码关联,改实现、换测试桩、加新功能都更轻松。
构造器注入:推荐首选,保障对象一创建就完整
在类的构造方法中声明依赖,Spring 创建 Bean 时直接传入。这种方式天然支持不可变性(可配合 private final 字段),强制所有必需依赖必须提供,避免空指针和运行时异常。
- 适合:强依赖、不可为空的核心服务(如 UserService、DataSource)
- 写法示例:@Service
public class OrderService {
private final UserService userService;
private final PaymentService paymentService;
public OrderService(UserService userService, PaymentService paymentService) {
this.userService = userService;
this.paymentService = paymentService;
}
} - 注意:Spring 5.3+ 默认对单参构造器也支持隐式 @Autowired,多参则无需额外注解
Setter 注入:适合可选或后期可重置的依赖
通过 public 的 setter 方法暴露依赖入口,Spring 在 Bean 初始化后调用该方法完成赋值。灵活性高,但对象可能处于“半初始化”状态。
- 适合:可选依赖(如日志上报开关)、需要运行时动态替换的组件(如策略切换)
- 写法示例:private EmailService emailService;
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
} - 提醒:不能保证注入一定发生(比如被@Bean方法绕过),也不适用于 final 字段
字段注入(@Autowired 直接标在属性上):简洁但不推荐用于关键逻辑
写法最简,Spring 直接反射设置私有字段值。开发快,但牺牲了对象封装性、不可变性和可测试性。
- 问题明显:无法在单元测试中手动构造对象;无法使用 final;难以识别哪些是必需依赖
- 仅建议:在原型验证、简单工具类或 Spring Boot 的配置类中临时使用
- 反例写法:@Autowired
private SmsService smsService;(不推荐在业务 Service 中这样写)
配合接口编程,进一步松动实现绑定
解耦不只是“不 new”,更是“不依赖具体类”。定义 UserService 接口,让多个实现(MockUserServiceImpl、DbUserServiceImpl)可互换,Spring 根据类型或 @Qualifier 自动匹配。
- 好处:测试时注入 Mock 实现,生产时注入真实实现,无需改业务代码
- 关键点:@Service 实现类默认按类型注册;多个同类型 Bean 时,用 @Qualifier 指定名称
- 例如:@Service("mockUserService")
public class MockUserServiceImpl implements UserService { ... },注入处加 @Qualifier("mockUserService")
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











