Spring 的常用注入方式有哪些

Spring 的常用注入方式有哪些

Spring 提供了三种基本注入方式:构造器注入Setter 方法注入字段注入,此外还支持注入集合类型(ListMap)以及配置值(@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 可以将它们统一收集注入到 ListMap 中,这是策略模式、插件化扩展的常用手段。

注入 List

Spring 会将所有 MessageSender 类型的 Bean 收集后注入,顺序由 @OrderOrdered 接口决定

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 后高)
本文作者:
本文链接: https://hgnulb.github.io/blog/2026/56134052
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!