Skip to content

@Autowired vs @Resource vs @Inject 注入原理与区别

提出问题

Spring 开发中,依赖注入是最基本的操作,但注入注解有三种:@Autowired、@Resource、@Inject。很多同学只记得 @Autowired 是首选,但问到「为什么有时候 @Autowired 报错改成 @Resource 就好了」就说不清了。面试官问这道题,不是要你背三者的包名,而是考察你对注入流程的底层理解:Spring 容器如何查找候选 bean?byType 和 byName 的退化逻辑是什么?为什么 @Autowired 加在字段上不好,推荐构造器注入?这背后涉及 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor 的源码逻辑,是区分「会用」和「懂原理」的典型题。

分析问题

三种注解的查找策略

@Autowired(Spring 原生):默认按 byType 查找。当容器中存在多个同类型的 bean 时,自动退化为按 byName,即字段名或参数名与 bean 的 id 匹配。如果既找不到唯一类型,字段名也匹配不上,则抛出 NoUniqueBeanDefinitionException。可以通过 @Qualifier 显式指定 bean 名称,配合 @Primary 可以设置默认首选项。

java
@Component
public class OrderService {
    // 按类型查找 UserDao,如果存在两个 UserDao 实现,按字段名 "userDao" 匹配
    @Autowired
    private UserDao userDao;

    // 显式指定 bean 名称,避免歧义
    @Autowired
    @Qualifier("userDaoOracle")
    private UserDao userDaoOracle;

    // 构造器注入(推荐方式),Spring 4.3+ 单个构造器可省略 @Autowired
    private final PaymentService paymentService;
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

@Resource(JSR-250 规范):默认按 byName 查找,可以通过 name 属性指定 bean 名称。如果按 name 找不到,回退为 byType。没有 @Qualifier 类似的配合注解,但 name 属性本身就是按名查找。

java
@Component
public class InventoryService {
    @Resource(name = "redisCache")
    private CacheService cacheService;

    // 不指定 name,默认按字段名 "cacheService" 查找
    @Resource
    private CacheService cacheService;
}

@Inject(JSR-330 规范):行为与 @Autowired 高度一致,默认 byType,配合 @Named 指定名称。区别在于 @Inject 没有 required 属性,无法控制注入是否必须。Spring 3.0+ 支持 @Inject,但需要额外引入 javax.inject(Jakarta EE 9+ 迁移到 jakarta.inject)。

java
@Component
public class ReportService {
    // 与 @Autowired 等效,但无 required=false 选项
    @Inject
    private DataSource dataSource;

    // 配合 @Named 相当于 @Autowired + @Qualifier
    @Inject
    @Named("oracleDataSource")
    private DataSource oracleDataSource;
}

三者的源码入口差异

@Autowired 的处理入口是 AutowiredAnnotationBeanPostProcessor,它实现了 InstantiationAwareBeanPostProcessor 接口,在 postProcessProperties 阶段遍历所有字段和方法,调用 beanFactory.resolveDependency() 按类型查找。当类型匹配多个时,内部调用 DefaultListableBeanFactory.determinePrimaryCandidate() 检查是否有 @Primary,没有则根据字段名调用 determineAutowireCandidate() 过滤。

@Resource 的处理入口是 CommonAnnotationBeanPostProcessor,它继承自 InitDestroyAnnotationBeanPostProcessor,在 postProcessProperties 中先判断 name 属性,有则直接调用 beanFactory.getBean(name) 按名获取;没有则调用 beanFactory.getBean(fieldName) 尝试按名;BeanFactory 中没有该名称的 bean 时,回退到 beanFactory.resolveDependency() 按类型查找。

@Inject 的处理入口也是 AutowiredAnnotationBeanPostProcessor(Spring 内部将 @Inject 视为与 @Autowired 同等的注入点),源码中 AutowiredAnnotationBeanPostProcessor 能识别 javax.inject.Inject 注解和 org.springframework.beans.factory.annotation.Autowired 注解。

java
// AutowiredAnnotationBeanPostProcessor 核心判断逻辑
// 判断注入点是否为 @Autowired 或 @Inject
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
    // 检查 @Autowired、@Value、@Inject 注解
    if (annotationType == Autowired.class ||
        annotationType == Value.class ||
        annotationType == Inject.class) {
        // 统一当作 Autowired 注入点处理
    }
}

推荐用构造器注入而非字段注入

Spring 官方文档和 Spring 团队(如 Josh Long)一直推荐构造器注入,理由有三:

  1. 不可变性:构造器注入的依赖可以用 final 修饰,确保初始化后不可变,而字段注入无法做到。
  2. 循环依赖检测:构造器注入在容器启动阶段就能检测到循环依赖(抛 BeanCurrentlyInCreationException),而字段注入要到运行时才能发现,排查成本高。
  3. 单元测试友好:构造器注入直接通过 new 传参即可测试,不需要反射或 Mockito 的 @InjectMocks 绕开私有字段。
java
// 推荐:构造器注入
@Component
public class PaymentService {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;

    public PaymentService(OrderRepository orderRepository,
                          PaymentGateway paymentGateway) {
        this.orderRepository = orderRepository;
        this.paymentGateway = paymentGateway;
    }
}

// 不推荐:字段注入
@Component
public class PaymentServiceBad {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private PaymentGateway paymentGateway;
}

总结

特性@Autowired@Resource@Inject
规范来源Spring 原生JSR-250JSR-330
默认策略byType → byNamebyName → byTypebyType(同 @Autowired)
指定名称@Qualifiername 属性@Named
required=false支持不支持不支持
建议使用构造器注入字段注入(兼容性)切 Spring 框架时

生产避坑:多实现类场景下,优先用 @Autowired + @Qualifier 或构造器注入,避免 @Resource 的 byName 回退行为导致预期外的 bean 注入。Spring 6/Spring Boot 3 中 @Inject 从 javax.inject 迁移到 jakarta.inject,升级时注意包名变更。

参考:Spring 源码 — AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor;JSR-330 规范;Spring 团队博客 — Constructor Injection

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。