@Autowired 和 @Resource 注解的区别?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

面试官问出这个问题,主要想考察以下几个层面:

  1. 对主流依赖注入(DI)注解的基础掌握:候选人是否清楚这两个注解的基本用途,即它们都是用来进行依赖注入的。
  2. 对两者核心差异的精准理解:这不仅仅是记住区别,更是考察候选人是否理解 Spring 框架与 Java EE 标准之间的关系,以及不同设计哲学下的实现选择。
  3. 在实际开发中的选型和应用能力:面试官想知道你是否能根据具体场景(例如,按类型还是按名称注入,是否需要处理多Bean候选者)做出合适的选择,并了解其背后的原理。
  4. 是否关注过最佳实践和版本演进:例如,是否了解 Spring 官方当前更推荐构造函数注入,以及如何避免注解使用的常见陷阱。

核心答案

@Autowired@Resource 都是用来完成依赖注入的注解,但它们在来源、默认注入方式、以及处理 “有多个匹配Bean” 时的策略上存在关键区别。

特性@Autowired@Resource
来源Spring 框架原生注解 (org.springframework.beans.factory.annotation)Java EE(JSR-250)标准注解 (javax.annotation),现由 Jakarta EE 维护。Spring 对其提供了支持。
默认注入方式按类型(byType)按名称(byName)。如果未指定名称,则回退到按类型。
处理“多个Bean”的方式1. 与 @Qualifier 注解配合,指定 Bean 的名称。
2. 如果存在多个同类型 Bean,必须通过上述方式消除歧义,否则会抛出 NoUniqueBeanDefinitionException
1. 优先使用其 name 属性指定的名称进行匹配。
2. 若未指定 name,则使用字段或 setter 方法的名称作为 Bean 名查找。
3. 若按名称未找到,才回退到按类型,此时若仍有多个,则报错。
是否支持 required支持。@Autowired(required = false) 表示注入是可选的,找不到 Bean 时不会报错(注入 null)。不支持。但可以通过结合 @Nullable(Spring 5+)或使用 Java 8 的 Optional 包装达到类似效果。
适用场景典型 Spring 项目,强调按类型注入的松耦合。需要按名称注入,或希望代码对 Spring 框架的依赖更轻(因为是标准注解)。

简单来说:想按类型注入用 @Autowired,想按名称注入用 @Resource

深度解析

原理与机制

@Autowired 由 Spring 的 AutowiredAnnotationBeanPostProcessor 处理。它的核心逻辑是 “按类型匹配”。在 Spring 容器启动、装配 Bean 时,它会寻找与目标字段/方法参数类型匹配的 Bean。这个过程是 Spring 依赖注入的核心体现。

@ResourceCommonAnnotationBeanPostProcessor 处理。它的处理逻辑更符合 Java EE 的直觉:先按名,再按型。这种设计使得它在处理一些名称明确、需要精准指向的场景时更加直观。

代码示例

假设我们有以下两个同类型的 Bean:

@Repository("userDaoImplA") // 指定 Bean 名称为 “userDaoImplA”
public class UserDaoImplA implements UserDao { ... }

@Repository("userDaoImplB") // 指定 Bean 名称为 “userDaoImplB”
public class UserDaoImplB implements UserDao { ... }

场景一:使用 @Autowired

@Component
public class UserService {
    // 方式1:直接使用,会因找到两个 UserDao 类型的 Bean 而报错。
    // @Autowired
    // private UserDao userDao;

    // 方式2:正确方式,配合 @Qualifier 指定名称
    @Autowired
    @Qualifier("userDaoImplA")
    private UserDao userDao;
}

场景二:使用 @Resource

@Component
public class UserService {
    // 方式1:使用 name 属性明确指定
    @Resource(name = "userDaoImplA")
    private UserDao userDao;

    // 方式2:不指定 name,则使用字段名 `userDao` 去查找 Bean,找不到,报错。
    // @Resource
    // private UserDao userDao; // 错误!

    // 方式3:将字段名改为与其中一个 Bean 名称一致
    @Resource
    private UserDao userDaoImplA; // 成功注入 UserDaoImplA
}

最佳实践与注意事项

  1. 现代 Spring 开发的首选:对于必须的依赖,Spring 官方推荐使用构造函数注入(在构造方法上使用 @Autowired,Spring 4.3 后单构造方法可省略)。这种方式能保证依赖不可变,且便于测试。
  2. 清晰的命名:使用 @Resource 时,保持注入的字段名与目标 Bean 的名称一致,可以使代码意图更清晰。
  3. 避免混用:在同一个项目中,建议团队统一注入风格,避免 @Autowired@Resource 随意混用,增加理解和维护成本。
  4. 可选依赖:对于可选的依赖,使用 @Autowired(required = false)Optional<T> 进行包装是更 Spring 风格的做法。

常见误区

  • 误区一:认为 @Resource 是 Spring 注解。它是 Java/Jakarta EE 标准,Spring 只是 “支持” 它。了解这一点有助于理解其设计初衷(与 EJB 等传统 Java EE 组件兼容)。
  • 误区二:认为 @Resource 一定按名称注入。准确说,它是 “优先” 按名称,名称找不到时会回退到按类型。如果按类型找到多个,同样会失败。
  • 误区三:过度依赖字段注入。无论是 @Autowired 还是 @Resource,直接标注在字段上虽然方便,但会绕过 Spring 的实例化流程(如通过反射直接设置),不利于封装和测试。构造函数注入或 Setter 注入是更优的选择。

总结

@Autowired 是 Spring 亲生的 “按类型” 注入能手,需要搭配 @Qualifier 来处理名称问题;而 @Resource 是 Java 标准出身的“先名后型”多面手。理解它们的区别,能帮助你在 Spring 项目中更优雅、更精准地进行依赖管理。在实际开发中,遵循构造函数注入的现代最佳实践,能让你的代码更加健壮和清晰。