依赖注入方式与 @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/事务章节的代理语义打好基础。