穿越噩梦中文正式版
4.35G · 2025-10-27
经常用IDEA写代码的朋友都知道,用@Autowired依赖注入总是会有如下一堆警告。
于是很多朋友改用@Resource。警告是没了,但实际上Spring官方最推荐的即不是@Autowired也不是@Resource,而是构造器注入。
今天咱就来掰扯清楚,保证让你看完能明明白白!
@Autowired写法(字段注入):
@Service
public class UserService {
@Autowired // 直接写在字段上
private UserMapper userMapper;
public void getUser() {
userMapper.findUser(); // 可能空指针!
}
}
@Resource写法:
@Service
public class UserService {
@Resource // JSR-250标准注解
private UserMapper userMapper;
}
构造器注入写法:
@Service
public class OrderService {
private final UserService userService;
public OrderService(UserService userService) {
this.userService = userService;
}
public void createOrder() {
userService.saveLog("下单了");
}
}
构造器方式可以更简洁点,用Lombok,加个@RequiredArgsConstructor注解:
@Service
@RequiredArgsConstructor
public class OrderService {
private final UserService userService;
public void createOrder() {
userService.saveLog("下单了");
}
}
来看一个例子:
上周我们线上出了事故,原因是因为@Autowired注入为null!
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // 可能为null!
public void createOrder() {
paymentService.pay(); // 这里报空指针!
}
}
为什么为null?
Spring容器启动时,Bean的创建顺序不确定OrderService可能比PaymentService先创建paymentService就是null如果用构造器注入:
@Service
public class OrderService {
private final PaymentService paymentService;
private final LogisticsService logisticsService;
// 构造器注入
public OrderService(PaymentService paymentService,
LogisticsService logisticsService) {
// 如果paymentService为null,这里直接报错!
this.paymentService = paymentService;
this.logisticsService = logisticsService;
}
public void createOrder() {
paymentService.pay(); // 这里是绝对安全的!
logisticsService.ship();
}
}
构造器注入的四大好处:
简单总结:
@Autowired:Spring早期推荐的方式,按类型(byType)注入@Resource:Java标准注解,按名称(byName)注入看个实际案例(代码注释有说明):
public interface MessageService {
void send();
}
@Service("smsService") // 指定bean名称
public class SmsService implements MessageService {
public void send() { System.out.println("发短信"); }
}
@Service("emailService") // 另一个实现
public class EmailService implements MessageService {
public void send() { System.out.println("发邮件"); }
}
// 案例1:@Autowired会报错
@Service
public class NotificationService {
@Autowired // 报错!找到两个MessageService实现
private MessageService messageService;
}
// 案例2:@Resource可以指定名称
@Service
public class NotificationService {
@Resource(name = "smsService") // 明确要哪个
private MessageService messageService;
}
// 案例3:@Autowired+@Qualifier也可以
@Service
public class NotificationService {
@Autowired
@Qualifier("emailService") // 配合使用
private MessageService messageService;
}
记住这个选择指南:
@Service
public class UserService {
private final UserMapper userMapper; // 必需
private final OrderService orderService; // 必需
public UserService(UserMapper userMapper, OrderService orderService) {
this.userMapper = userMapper;
this.orderService = orderService;
}
}
@Service
public class UserService {
@Autowired(required = false) // 可选的,没有也不会报错
private BonusService bonusService;
}
@Service
public class UserService {
@Resource(name = "wechatNotify") // 明确要微信通知
private NotifyService notifyService;
}
@Service
public class UserService {
private ConfigService configService;
@Autowired // 写在setter方法上
public void setConfigService(ConfigService configService) {
this.configService = configService;
}
}
来看一个完整的用户服务类就一目了然了:
@Service
public class UserService {
// 必需的核心依赖:构造器注入
private final UserMapper userMapper;
private final OrderService orderService;
// 可选的增强功能:@Autowired
@Autowired(required = false)
private VIPService vipService;
// 多个通知实现:用@Resource指定
@Resource(name = "smsNotify")
private NotifyService notifyService;
// 配置信息:setter注入
private ConfigService configService;
// 构造器注入必需依赖
public UserService(UserMapper userMapper, OrderService orderService) {
this.userMapper = userMapper;
this.orderService = orderService;
}
// setter注入可选配置
@Autowired
public void setConfigService(ConfigService configService) {
this.configService = configService;
}
public void register(User user) {
userMapper.save(user);
orderService.initUserOrder(user);
if (vipService != null) {
vipService.activateTrial(user);
}
notifyService.sendWelcome(user);
}
}
构造器的注入可以让单元测试变得超级简单:
// 生产代码
@Service
public class UserService {
private final UserMapper userMapper;
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
// 测试代码
public class UserServiceTest {
@Test
public void testUserService() {
// 直接new,不用Mockito注解
UserMapper mockMapper = Mockito.mock(UserMapper.class);
UserService userService = new UserService(mockMapper);
// 写测试用例...
}
}
| 场景 | 推荐方式 | 示例 |
|---|---|---|
| 必需依赖 | 构造器注入 | public UserService(UserMapper mapper) |
| 可选依赖 | @Autowired(required=false) | @Autowired(required=false) |
| 按名称注入 | @Resource(name="xxx") | @Resource(name="smsService") |
| 可选配置 | Setter注入 | @Autowired public void setXxx() |
黄金法则:
现在咱们应该都明白了吧?从今天开始,试着用构造器注入你的代码,保证代码的健壮性!
《工作 5 年没碰过分布式锁,是我太菜还是公司太稳?网友:太真实了!》
《90%的人不知道!Spring官方早已不推荐@Autowired?这3种注入方式你用对了吗?》
《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》
《终于找到 Axios 最优雅的封装方式了,再也不用写重复代码了》