Spring中@Autowired和@Resource的区别
1. 基本区别
特性 | @Autowired | @Resource |
---|---|---|
来源 | Spring框架提供 | JSR-250规范提供,Java标准 |
装配顺序 | 优先按类型装配 | 优先按名称装配 |
默认匹配规则 | 默认按类型匹配,可以使用@Qualifier指定名称 | 默认按名称匹配,如果无法匹配则按类型匹配 |
属性 | required属性可以设置是否必须注入成功 | name属性可以显式指定bean名称 |
适用性 | Spring特有功能 | 标准Java注解,有更好的框架兼容性 |
2. 注解来源
- @Autowired:来自Spring框架(
org.springframework.beans.factory.annotation.Autowired
) - @Resource:来自Java标准JSR-250(
javax.annotation.Resource
/jakarta.annotation.Resource
)
3. 装配机制对比
@Autowired的装配过程
@Autowired默认按照类型(by-type)装配依赖对象:
public class UserService {
@Autowired
private UserRepository userRepository; // Spring会在容器中查找类型为UserRepository的bean进行注入
}
当容器中存在多个相同类型的bean时,会导致异常。此时可以使用@Qualifier
注解指定bean的名称:
public class UserService {
@Autowired
@Qualifier("mysqlUserRepository")
private UserRepository userRepository; // 指定名称为mysqlUserRepository的bean
}
或者可以设置required=false
使得依赖变为非必须:
public class UserService {
@Autowired(required = false)
private UserRepository userRepository; // 如果找不到匹配的bean,不会抛出异常
}
@Resource的装配过程
@Resource默认按照名称(by-name)装配依赖对象:
public class UserService {
@Resource // 默认查找名为"userRepository"的bean(字段名)
private UserRepository userRepository;
}
可以通过name属性显式指定bean名称:
public class UserService {
@Resource(name = "mysqlUserRepository")
private UserRepository userRepository;
}
@Resource的装配规则:
- 如果指定了name属性,则按照指定的name查找bean
- 如果没有指定name属性,则使用字段名作为bean名称查找
- 如果按名称未找到匹配的bean,则按类型进行查找
- 如果找到多个匹配的bean,则抛出异常
4. 实际案例分析
假设我们有以下接口和实现类:
public interface MessageService {
String getMessage();
}
@Component("emailService")
public class EmailServiceImpl implements MessageService {
@Override
public String getMessage() {
return "Email Message";
}
}
@Component("smsService")
public class SmsServiceImpl implements MessageService {
@Override
public String getMessage() {
return "SMS Message";
}
}
场景1:使用@Autowired注入
@Service
public class NotificationService {
@Autowired
private MessageService messageService; // 报错:找到多个MessageService类型的bean
public void sendNotification() {
System.out.println(messageService.getMessage());
}
}
这种情况下,Spring容器会抛出NoUniqueBeanDefinitionException
异常,因为存在两个MessageService类型的bean。
修复方法1 - 使用@Qualifier:
@Service
public class NotificationService {
@Autowired
@Qualifier("emailService")
private MessageService messageService; // 指定使用emailService
public void sendNotification() {
System.out.println(messageService.getMessage());
}
}
修复方法2 - 将字段名与bean名称匹配(但这不是@Autowired的默认行为):
@Service
public class NotificationService {
@Autowired
private MessageService emailService; // 字段名与bean名称匹配,但@Autowired仍按类型匹配
public void sendNotification() {
System.out.println(emailService.getMessage());
}
}
场景2:使用@Resource注入
@Service
public class NotificationService {
@Resource // 默认按照字段名"messageService"查找bean,找不到则按类型查找
private MessageService messageService; // 报错:按类型找到多个bean
public void sendNotification() {
System.out.println(messageService.getMessage());
}
}
直接指定name属性:
@Service
public class NotificationService {
@Resource(name = "emailService")
private MessageService messageService; // 明确指定使用emailService
public void sendNotification() {
System.out.println(messageService.getMessage());
}
}
让字段名与bean名一致:
@Service
public class NotificationService {
@Resource
private MessageService emailService; // 字段名为emailService,@Resource会按此名称查找
public void sendNotification() {
System.out.println(emailService.getMessage());
}
}
5. 为什么阿里巴巴Java规范推荐使用@Resource
阿里巴巴Java开发手册中明确建议:
推荐使用@Resource注解,而非@Autowired注解。
原因主要有以下几点:
5.1 标准性和兼容性
@Resource是Java标准(JSR-250)的一部分,而@Autowired是Spring框架特有的。使用标准注解可以提高代码的可移植性,降低对特定框架的依赖性。
5.2 更安全的依赖注入方式
@Resource默认按名称装配,在大型项目中更不容易出错:
- 按名称注入更加明确,不容易因为类型相同而导致注入错误
- 使用@Autowired时,如果有多个相同类型的bean,必须额外使用@Qualifier
- 在重构时,@Resource的按名称匹配可以提供更好的稳定性
5.3 更清晰的表达意图
使用@Resource(name=“beanName”)的方式可以更清晰地表达开发者的意图,即明确指定要注入哪个bean,减少了维护者的理解成本。
5.4 代码示例对比
不推荐的方式(@Autowired):
@Service
public class OrderService {
@Autowired // 如果有多个UserRepository实现,此处可能注入错误
private UserRepository userRepository;
@Autowired
@Qualifier("redisCache") // 需要额外的注解指定名称
private Cache cache;
}
推荐的方式(@Resource):
@Service
public class OrderService {
@Resource(name = "mysqlUserRepository") // 明确指定要注入的bean
private UserRepository userRepository;
@Resource(name = "redisCache") // 直接通过name属性指定
private Cache cache;
}
6. 结论
- @Autowired是Spring特有的注解,主要按类型装配,需要配合@Qualifier使用
- @Resource是Java标准注解,主要按名称装配,更符合依赖注入的设计理念
- 阿里巴巴Java规范推荐使用@Resource的主要原因是:
- 符合Java标准规范,提高代码可移植性
- 按名称装配更加明确,减少大型项目中的注入错误
- 表达意图更清晰,降低维护成本
在实际开发中,推荐使用@Resource并明确指定name属性,使依赖关系更加清晰,以提高代码的可维护性和健壮性。