Spring 的常用注入方式有哪些
Spring 的常用注入方式有哪些
Spring 提供了三种基本注入方式:构造器注入、Setter 方法注入、字段注入,此外还支持注入集合类型(
List、Map)以及配置值(@Value)。
构造器注入(推荐)
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired // Spring 4.3+ 只有一个构造器时可省略
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
配合 Lombok 简化:
@Component
@RequiredArgsConstructor // 只为 final 字段生成构造器,比 @AllArgsConstructor 更精准
public class UserService {
private final UserRepository userRepository;
}
基于 Java 配置的写法:
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepository();
}
@Bean
public UserService userService(UserRepository userRepository) {
// Spring 会自动将 userRepository() 的返回值注入进来
return new UserService(userRepository);
}
}
优点:
- 依赖声明为
final,不可变,天然线程安全- 对象创建时依赖已完整注入,保证对象一致性
- 依赖关系一目了然,便于单元测试(直接
new)- 能有效防止循环依赖(Spring 会在启动时报错提醒)
缺点:
- 依赖过多时构造器参数较长(但这往往是类职责过重的信号,应拆分)
Setter 方法注入
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
基于 Java 配置的写法:
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
UserService userService = new UserService();
userService.setUserRepository(userRepository());
return userService;
}
@Bean
public UserRepository userRepository() {
return new UserRepository();
}
}
优点:
- 适合可选依赖(不注入也能正常运行)
- 依赖可以在对象创建后重新设置(适合动态替换场景)
缺点:
- 依赖可能为
null,需要防御性校验- 字段不能声明为
final
字段注入
直接在字段上标注
@Autowired或@Resource。
// @Autowired:按类型匹配,找到多个时再按名称匹配
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
}
// @Resource:默认按名称匹配,找不到再按类型匹配(JSR-250 标准,非 Spring 专属)
@Component
public class UserService {
@Resource
private UserRepository userRepository;
}
优点:
- 代码最简洁,开发速度快
缺点:
- 依赖隐藏在类内部,单元测试无法通过构造器直接注入 mock
- 字段不能声明为
final- IDE(如 IntelliJ IDEA)会给出警告,Spring 官方不推荐
@Autowired 与 @Resource 的区别
| 对比项 | @Autowired |
@Resource |
|---|---|---|
| 来源 | Spring 专属 | JSR-250 标准(Java 内置) |
| 匹配策略 | 先按类型,再按名称 | 先按名称,再按类型 |
| 指定名称 | 配合 @Qualifier("name")
|
@Resource(name = "name") |
| 是否支持必填 |
required = false 可选 |
不支持,找不到直接报错 |
| 适用范围 | 字段、构造器、方法 | 字段、setter 方法 |
注入集合类型
当容器中存在同一类型的多个 Bean 时,Spring 可以将它们统一收集注入到
List或Map中,这是策略模式、插件化扩展的常用手段。
注入 List
Spring 会将所有
MessageSender类型的 Bean 收集后注入,顺序由@Order或Ordered接口决定。
public interface MessageSender {
void send(String message);
}
@Component
@Order(1)
public class EmailSender implements MessageSender {
public void send(String message) { /* 发邮件 */ }
}
@Component
@Order(2)
public class SmsSender implements MessageSender {
public void send(String message) { /* 发短信 */ }
}
@Component
@RequiredArgsConstructor
public class NotificationService {
// 自动收集所有 MessageSender 实现,按 @Order 排序
private final List<MessageSender> senders;
public void notifyAll(String message) {
senders.forEach(sender -> sender.send(message));
}
}
典型应用场景: 责任链、过滤器链、插件列表。
注入 Map
Spring 会以 Bean 名称为 key、Bean 实例为 value 注入,方便按名称动态路由。
@Component
@RequiredArgsConstructor
public class PaymentRouter {
// key = Bean 名称(如 "alipayService"),value = Bean 实例
private final Map<String, PaymentService> paymentServices;
public void pay(String channel, BigDecimal amount) {
PaymentService service = paymentServices.get(channel + "Service");
if (service == null) {
throw new IllegalArgumentException("不支持的支付渠道:" + channel);
}
service.pay(amount);
}
}
典型应用场景: 策略路由(按渠道、类型动态选择实现)、工厂模式替代。
注意: 注入
Map<String, T>时 key 固定为 Bean 名称(即@Component的 value 或方法名),需要保证命名规范。
注入配置值(@Value)
@Value用于注入配置文件中的属性值,属于值注入而非 Bean 注入。
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.timeout:5000}") // 带默认值,配置不存在时用 5000
private int timeout;
@Value("${app.servers}") // 配置:app.servers=host1,host2,host3
private List<String> servers; // Spring 自动按逗号分割为 List
}
三种注入方式对比
| 对比项 | 构造器注入 | Setter 注入 | 字段注入 |
|---|---|---|---|
| 依赖是否可变 | 不可变(final) | 可变 | 可变 |
| 是否支持可选依赖 | 不支持 | 支持 | 支持 |
| 单元测试便利性 | 高(直接 new) | 中 | 低(需反射注入) |
| 循环依赖检测 | 启动时报错 | 运行时才暴露 | 运行时才暴露 |
| Spring 推荐 | ✅ 推荐 | 可选依赖时使用 | ❌ 不推荐 |
| 代码简洁度 | 中(Lombok 后高) | 低 | 高 |
本博客所有文章除特别声明外,均采用
CC BY-NC-SA 4.0
许可协议,转载请注明出处!