Java 实用注解篇:@Autowired vs @Resource,为什么不推荐使用 @Autowired?

前言

        在 Spring Boot 开发中,依赖注入(Dependency Injection,简称 DI)是核心概念,@Autowired@Resource 都是常见的注解。然而,在实际开发中,并不推荐 直接使用 @Autowired,而是推荐 构造方法注入使用 @Resource 代替 @Autowired

        本篇博客将 深入解析 @Autowired@Resource 的区别、原理、优缺点,并探讨为什么 Spring 社区和许多开发者 不推荐 直接使用 @Autowired 进行依赖注入。


一、@Autowired 介绍

1️⃣ 什么是 @Autowired?

@Autowired 是 Spring 提供的 自动依赖注入(Dependency Injection, DI)注解,能够自动将符合条件的 Bean 装配到需要的地方,而不需要手动 new 对象。

2️⃣ @Autowired 的基本使用

        Spring 允许 三种方式 进行 @Autowired 依赖注入:

        ① 直接作用于字段(不推荐 ❌)
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public void getUser() {
        userRepository.findUser();
    }
}

缺点

  • 破坏单元测试(无法直接传入 Mock 对象)。
  • 不易维护(无法追踪 Bean 依赖)。
  • 可能导致循环依赖

② 作用于 Setter 方法(适用于可选依赖 ✅)

@Service
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

适用场景

  • 适用于 可选依赖,例如某个 Bean 可能不一定需要。
  • 允许 运行时动态修改 Bean

③ 作用于构造方法(推荐方式 ✅✅)

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

优点

  • 提高安全性final 使得 userRepository 只能在构造方法中赋值)。
  • 更适用于单元测试(可以直接传入 Mock 对象)。
  • 减少反射使用,提高性能

二、@Autowired 的源码解析

1️⃣ @Autowired 的注解定义

在 Spring 中,@Autowired 的源码如下:

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}
  • @Target 说明可以用在 构造方法、字段、Setter 方法
  • @Retention(RetentionPolicy.RUNTIME) 说明它在 运行时生效
  • boolean required() default true; 说明 依赖是必须的,如果没有找到对应的 Bean,Spring 会报错。

2️⃣ Spring 解析 @Autowired 过程

Spring 在 AutowiredAnnotationBeanPostProcessor处理 @Autowired 注解

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    if (beanClass.isAnnotationPresent(Autowired.class)) {
        // 获取构造方法
        Constructor<?>[] constructors = beanClass.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.isAnnotationPresent(Autowired.class)) {
                // 使用 Spring 容器查找对应的 Bean 进行注入
                return beanFactory.createBean(beanClass);
            }
        }
    }
    return null;
}

3️⃣ 解析流程

  1. 扫描 Spring 容器中的所有 Bean,找到带有 @Autowired 的字段、方法或构造方法。
  2. 判断是否有匹配的 Bean,如果找到了,就自动注入。
  3. 如果没有找到 Bean,而 required = true,则报错 NoSuchBeanDefinitionException

三、@Resource 介绍

1️⃣ 什么是 @Resource?

@Resource 是 Java 标准(JSR-250)中的 依赖注入注解,由 javax.annotation.Resource 提供。在 Spring 框架中,@Resource 也可以用于 自动注入 Bean,类似于 @Autowired

2️⃣ @Resource 的基本用法

@Service
public class UserService {
    @Resource
    private UserRepository userRepository;
}

@Resource 默认按照 Bean 名称注入,如果找不到匹配的 Bean 名称,则按 类型 进行注入。


四、@Autowired vs @Resource:详细对比

比较项@Autowired@Resource
所属标准Spring 框架JSR-250(Java 标准)
默认匹配方式按类型(byType)按名称(byName)
是否支持 @Primary✅ 支持❌ 不支持
是否支持 @Qualifier✅ 支持✅ 支持
是否必须依赖 Spring✅ 是❌ 不是(可用于 JavaEE)
推荐方式构造方法注入字段注入(不推荐)

五、为什么不推荐 @Autowired 直接注入?

虽然 @Autowired 很方便,但以下几个原因 不建议直接使用

1️⃣ 破坏代码的可测试性

直接使用 @Autowired 导致单元测试非常困难,因为它 强依赖 Spring 容器

public class UserServiceTest {
    @Autowired  // ❌ 测试时无法直接注入 Mock 对象
    private UserService userService;
}

解决方案:使用 构造方法注入

public class UserServiceTest {
    private UserService userService;

    @BeforeEach
    void setUp() {
        UserRepository userRepository = mock(UserRepository.class);
        userService = new UserService(userRepository);  // ✅ 直接传入 Mock 对象
    }
}

这样可以 不依赖 Spring 容器 进行测试。

2️⃣ 不能保证 Bean 是不可变的

字段注入

@Autowired
private UserRepository userRepository;

 这个字段 可以随时被修改,但如果使用 final 关键字,就能保证不可变:

private final UserRepository userRepository;

@Autowired 无法和 final 配合使用,但 构造方法注入可以

public UserService(final UserRepository userRepository) {
    this.userRepository = userRepository;
}

使用 final + 构造方法,可以保证 userRepository 只能初始化一次,不能修改

3️⃣ 代码可读性差

如果在 多个地方 直接使用 @Autowired,会导致代码的 依赖关系不清晰

@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@Autowired
private UserRepository userRepository;

✅ 这样一目了然,知道 UserService 依赖哪些组件:

public UserService(OrderService orderService, PaymentService paymentService, UserRepository userRepository) {
    this.orderService = orderService;
    this.paymentService = paymentService;
    this.userRepository = userRepository;
}

4️⃣ 难以管理 Bean 的生命周期

  • Spring 需要反射 去注入 @Autowired,影响性能。
  • Spring 容器初始化时,可能会遇到 循环依赖
@Service
public class AService {
    @Autowired
    private BService bService;
}

@Service
public class BService {
    @Autowired
    private AService aService;
}

解决方案:使用 构造方法注入 可以让 Spring 在构造时发现循环依赖。


六、最佳实践

注入方式推荐使用适用场景
@Autowired 直接注入❌ 不推荐破坏测试性,影响可读性
@Autowired 作用于 Setter✅ 部分推荐适用于 可选依赖
构造方法注入✅✅ 强烈推荐更安全、更可测试、更清晰
@Resource✅ 推荐适用于 J2EE 标准化开发


七、总结

  • @Autowired@Resource 都能 自动注入 Bean,但 @Autowired 默认按 类型,而 @Resource 默认按 名称
  • 不推荐使用 @Autowired 直接字段注入,因为 影响可测试性、可维护性、代码清晰度
  • 推荐使用构造方法注入,可以避免 循环依赖、提高代码安全性
  • @Resource 适用于 Java 标准化项目,可以 替代 @Autowired

如果觉得这篇博客对你有帮助,记得点赞 ⭐、收藏 📌、关注 🚀!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stay Passion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值