依赖注入方式与 @Autowired/@Resource/@Qualifier

引言

Bean 注册好之后,如何把依赖“塞”进去?本章梳理三条注入通道(构造器/setter/字段)与两个核心注解(@Autowired@Resource),以及候选 Bean 不只一个时的裁决工具 @Qualifier/@Primary

核心概念

  • 构造器注入:官方推荐的默认方式——依赖不可变、可空性一目了然、便于单元测试;类只有一个构造器时 Spring 会隐式自动装配,无需注解。
  • setter 注入:适合“可选依赖”,先建默认实例再补依赖,可配合 required=false 语义使用。
  • 字段注入:写起来最短,但依赖对测试不可见、与容器强耦合,官方不推荐,仅适合框架胶水代码。
  • @Autowired(org.springframework.beans.factory.annotation):先按类型匹配;匹配到多个候选时再按字段名/参数名、@Primary@Qualifier 依次裁决。
  • @Resource(jakarta.annotation.Resource,6.x 用 jakarta 命名空间):先按 Bean 名称匹配(默认取字段名/name 属性),找不到再回退按类型。
  • @Qualifier:与 @Autowired 组合,按限定名精确指定候选;也可自定义 @Qualifier 元注解做分组。
  • 集合注入:List<X>/Map<String, X> 会把同类型所有候选一次注入(官方支持)。

代码示例

public interface Notifier { void notify(String msg); }

@Component
@Primary // 无 @Qualifier 时的默认候选
public class SmsNotifier implements Notifier {
    public void notify(String msg) { System.out.println("[sms] " + msg); }
}

@Component("mail")
public class MailNotifier implements Notifier {
    public void notify(String msg) { System.out.println("[mail] " + msg); }
}

@Service
public class OrderService {
    private final Notifier notifier;          // 构造器注入,取 @Primary 的 SmsNotifier

    private List<Notifier> all;               // 集合注入:两个实现都进来

    @Autowired
    @Qualifier("mail")                        // 字段注入 + 精确点名
    private Notifier mailNotifier;

    public OrderService(Notifier notifier) { this.notifier = notifier; }

    @Resource(name = "mail")                  // @Resource 按名称取 mail 这个 Bean
    public void setReportNotifier(Notifier n) { /* setter 注入示例 */ }
}

注意点

  • 只有一个构造器时无需写 @Autowired;多个构造器时要用 @Autowired 指定容器该用哪个。
  • @Autowired(required = false) 表示找不到候选也不报错,此时注入 null——慎用,易掩盖装配错误。
  • 同一字段上 @Autowired@Resource 不要混用;两者处理机制不同(后者由 CommonAnnotationBeanPostProcessor 处理)。
  • 构造器注入天然规避“先实例化后装配”的循环依赖(详见第 08 章),这也是官方推荐它的原因之一。
  • 6.x 中 @Resource/@PostConstruct 一律 jakarta.annotation.*

小结

按“构造器注入为主、setter 注入为辅、字段注入仅限胶水代码”的原则书写;类型冲突时用 @Primary 设默认、@Qualifier 点名、@Resource 按名注入。清晰的注入策略能让代码易测、易替换,也为后续 AOP/事务章节的代理语义打好基础。

笔记加载中…