← 返回题目列表

@Autowired 和 @Resource 有什么区别?多个同类型 Bean 如何注入?

高频 简单 第 1 / 30 题 更新于 2026/07/26
SpringAutowiredResourceQualifier

简化版

@Autowired 是 Spring 注解,主要按类型解析依赖;存在多个候选时可用 @Primary@Qualifier 或注入点名称消歧。@Resource 来自 Jakarta 标准注解,显式指定 name 时按名称查找,未指定时通常先采用字段名或属性名,并可回退到类型匹配。

详细版

@Autowired 可用于构造器、方法、字段和参数,单构造器从 Spring 4.3 起通常可以省略注解。只有一个候选时直接注入;多个候选时,@Primary 给出首选项,@Qualifier 缩小候选集合,注入点名称可在类型筛选后继续帮助匹配。

@Resource(name = "smsSender") 表达明确的按名依赖;未写 name 时,默认名称取字段名或 setter 属性名。如果默认名称没有对应 Bean,Spring 默认允许回退到按类型解析。@Resource 适用于字段或单参数 setter,不用于构造器和多参数方法。

完整版教学

一、@Autowired 先解决类型候选

interface MessageSender {}

@Component
class SmsSender implements MessageSender {}

@Component
class EmailSender implements MessageSender {}

此时直接注入 MessageSender 有两个候选,容器无法唯一决定。最明确的方式是使用限定符:

@Service
class NoticeService {
    private final MessageSender sender;

    NoticeService(@Qualifier("smsSender") MessageSender sender) {
        this.sender = sender;
    }
}

@Qualifier 不是脱离类型的纯名称查找,而是在类型匹配出来的候选中继续筛选。大型系统还可以自定义带业务语义的 qualifier 注解,避免到处散落字符串名称。

二、@Primary、名称和集合注入

给某个实现标记 @Primary,表示单值注入遇到多个同类型候选时优先选择它;注入点存在更具体的 Qualifier 时,限定条件仍会参与选择。不要依赖恰好只有一个实现,因为后续新增实现会让启动突然失败。

如果业务本来就需要所有实现,可以注入集合:

StrategyService(List<DiscountStrategy> strategies) {
    this.strategies = strategies;
}

Spring 会注入全部可用候选,并可结合 @OrderOrdered 控制列表顺序。也可以注入 Map<String, DiscountStrategy>,键通常是 Bean 名称。

三、@Resource 的名称语义

@Resource(name = "smsSender")
private MessageSender sender;

显式 name 表示目标 Bean 名称,即使字段叫 sender 也不影响。省略 name 时,字段注入默认使用字段名,setter 注入默认使用属性名;若默认名找不到,Spring 的常规处理器默认可回退到按类型查找。

Spring 6 使用 jakarta.annotation.Resource,旧项目常见的 javax.annotation.Resource 属于 Jakarta EE 迁移前的包名。升级时要根据 Spring 和依赖版本统一命名空间。

四、required 与可选依赖

@Autowired(required = false) 可以让整个注入点在无候选时跳过,但构造器注入更常使用 Optional<T>ObjectProvider<T>@Nullable 明确表达可选性。ObjectProvider 还支持延迟获取和遍历多个候选。

可选依赖不应掩盖配置错误。业务运行必须依赖的组件应保持必需,让应用在启动阶段尽早失败,而不是运行到某条请求才出现空指针。

五、为什么仍然优先构造器注入

无论采用何种候选选择规则,必需依赖都优先通过构造器表达:对象创建后就是完整状态、字段可设为 final、单元测试无需启动容器。@Resource 不支持构造器注入,因此需要构造器与标准注解风格时,也可考虑 Jakarta @Inject 配合 Spring 的 Qualifier 机制。

字段注入虽然简短,却隐藏依赖并增加脱离容器测试的难度。注解选择不是关键,依赖是否显式、候选是否唯一、失败是否尽早才是关键。

六、把候选决策画成一条链

面对 @Autowired MessageSender sender,不要把容器理解成“先猜名字”。它先找可赋值给 MessageSender 的候选,再结合 @Qualifier@Primary 等语义缩小范围;仍有多个候选时,注入点名称才可能作为进一步匹配条件。假设系统里有 smsSenderemailSenderpushSender 3 个实现,@Qualifier("smsSender") 会把 3 个候选收窄为 1 个,而不是绕过类型检查去取任意同名对象。

场景推荐写法选择结果
只有 1 个同类型 Bean构造器参数直接注入唯一候选
3 个实现,业务有默认项一个实现标 @Primary单值注入优先默认项
当前注入点必须指定实现@Qualifier("smsSender")在类型候选中精确筛选
需要执行全部策略List<MessageSender>注入全部候选,可排序
必须按固定 Bean 名取对象@Resource(name = "smsSender")按明确名称解析

记忆钩子:@Autowired 是“类型候选再消歧”,@Resource(name=...) 是“名称就是契约”;多实现时应主动表达选择,而不是依赖偶然的字段名。

七、常见误区与追问

  • 误区:@Autowired 永远只按类型,字段名完全不起作用。 类型筛选后若仍有多个候选,注入点名称可以作为候选匹配的一部分,但它不应替代明确的 @Qualifier
  • 误区:@Resource 未写 name 时只按名称,找不到就一定报错。 Spring 默认先使用字段名或属性名,并可回退到主类型匹配;显式写 name 时才是清晰的按名契约。
  • 误区:给任意一个实现加 @Primary 就能解决所有歧义。 @Primary 只为单值注入提供首选,集合注入仍会拿到全部候选,更具体的限定条件也会继续参与决策。
  • 追问:单构造器为什么可以不写 @Autowired? Spring 4.3 起,Bean 只有一个构造器时容器会直接使用它;有多个构造器时仍要遵守构造器选择规则。
  • 追问:List 和 Map 注入有什么区别? List 的元素是全部同类型 Bean,可按 @OrderOrdered 排序;Map<String, T> 的键通常是 Bean 名,适合按名称路由策略。
  • 追问:可选依赖怎样表达更清楚? 构造器参数可用 Optional<T>ObjectProvider<T> 或受支持的 @Nullable;必需依赖则应保持强约束,让配置错误在启动时暴露。

八、加强记忆

@Autowired 以类型候选为起点,用 @Primary@Qualifier 和注入点名称消歧;@Resource 显式 name 时按名,省略时从字段或属性名出发并可回退类型。多个实现不要碰运气:单选就明确限定,需要全部就注入 List 或 Map。