前言
在 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️⃣ 解析流程
- 扫描 Spring 容器中的所有 Bean,找到带有
@Autowired
的字段、方法或构造方法。 - 判断是否有匹配的 Bean,如果找到了,就自动注入。
- 如果没有找到 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
。
如果觉得这篇博客对你有帮助,记得点赞 ⭐、收藏 📌、关注 🚀!