构造函数注入是Spring官方推荐的、用于强制依赖的最佳实践;而
@Resource是Java标准中用于字段/Setter注入的注解,通常用于可选依赖或按名称装配。
1、核心对比
| 维度 | 构造函数注入 | @Resource 注入 |
|---|---|---|
| 所属规范 | Spring原生(推荐方式) | Java标准(JSR-250),与Spring解耦 |
| 依赖性质 | 强制依赖 ------ 对象创建时就必须提供 | 通常用于可选依赖 ------ 可以没有,注入失败只警告 |
| 不可变性 | 支持final字段,依赖不可变 |
字段不能是final,因为对象创建后才注入 |
| 单元测试 | 非常方便 ------ 直接new对象,传入Mock |
比较麻烦 ------ 需要启动Spring容器或使用反射 |
| 循环依赖 | 能检测并阻止 ------ 构造函数循环依赖会直接报错 | 可能允许 ------ Spring会尝试通过三级缓存解决,可能导致问题隐藏 |
| Null安全 | 安全 ------ 构造函数参数不能为null | 不安全 ------ 注入可能失败,导致运行时空指针 |
| 代码简洁度 | 构造函数参数较多时会显得冗长 | 字段上写一个注解,非常简洁 |
| 按名称装配 | 不直接支持,通常结合@Qualifier |
原生支持 ------ @Resource(name = "xxx") |
2、单元测试友好度(核心差异)
| 测试方式 | 构造函数注入 | @Resource |
|---|---|---|
| 纯 Java 测试 | ✅ new Service(mockA, mockB) |
❌ 必须借助 Spring 容器或反射 |
| Mockito 集成 | 直接传入 mock 对象 | 需 @MockBean + 启动容器,或反射注入 |
| 测试执行速度 | 毫秒级 | 秒级(容器启动) |
构造函数注入的测试
// 纯Java单元测试,无需Spring
UserRepository mockRepo = mock(UserRepository.class);
UserService service = new UserService(mockRepo); // 简单直接
@Resource字段注入的测试
// 方式1:启动Spring容器(慢)
@SpringBootTest
class UserServiceTest {
@MockBean UserRepository mockRepo;
@Autowired UserService service; // 依赖Spring来注入
}
// 方式2:反射(丑陋且脆弱)
UserService service = new UserService();
Field field = UserService.class.getDeclaredField("userRepository");
field.setAccessible(true);
field.set(service, mockRepo);
3、适用场景建议
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 强依赖、不可变服务 | 构造函数注入 | 强制提供、线程安全、测试简单 |
| 可选依赖、配置类 | Setter + @Autowired |
可以不传,灵活配置 |
| 按名称精确匹配 | @Resource |
避免 @Qualifier,名称即意图 |
| 遗留系统 / 简单 Demo | @Resource 字段注入 |
代码最少,快速开发 |
| 需要循环依赖(不推荐) | @Resource |
Spring 能处理,但应重构消除循环依赖 |
4、总结
构造函数注入是"刚需",让依赖关系清晰、安全、易测;@Resource是"便利贴",简洁但隐藏了依赖,更适合可选依赖或按名称装配的场景。日常开发中,优先使用构造函数注入。