@Resource
是 Java EE(现 Jakarta EE)标准中用于依赖注入的注解(JSR-250),Spring 框架完全支持该注解。它通过名称匹配或类型匹配自动注入依赖的 Bean,适用于需要显式指定依赖名称或跨框架集成的场景。以下从注解定义、源码解析、核心功能、使用场景及注意事项展开详细说明。
一、@Resource
注解的定义与源码解析
@Resource
位于 javax.annotation
包(Jakarta EE 9+ 后移至 jakarta.annotation
),其源码定义如下(简化版):
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Resource {
/**
* 资源名称(用于匹配 Bean 的名称,默认空)
*/
String name() default "";
/**
* 资源类型(用于匹配 Bean 的类型,默认空)
*/
Class<?> type() default Object.class;
}
关键属性说明:
name
:指定要注入的 Bean 名称(默认空,此时按类型匹配)。type
:指定要注入的 Bean 类型(默认Object.class
,此时按名称匹配)。
二、核心功能:依赖注入的匹配规则
@Resource
的核心功能是通过名称匹配或类型匹配定位目标 Bean,其匹配规则如下:
1. 优先按名称匹配
若 name
属性显式指定(非空),则优先查找名称完全匹配的 Bean(不区分大小写?不,Spring 中 Bean 名称是大小写敏感的)。
2. 名称不匹配时按类型匹配
若 name
为空或未找到匹配的 Bean,则按 type
属性指定的类型查找匹配的 Bean(支持继承关系,如接口与实现类)。
3. 无匹配时的行为
若既无名称匹配也无类型匹配的 Bean,且 @Resource
未标记为可选(Spring 中无 required
属性),则抛出 NoSuchBeanDefinitionException
。
三、典型使用场景与示例
1. 按名称注入(最常用)
当容器中存在多个同类型 Bean 时,通过 name
属性指定要注入的 Bean 名称:
示例:
// 配置类:定义两个同类型的 Bean(名称分别为 "mysqlDs" 和 "oracleDs")
@Configuration
public class DataSourceConfig {
@Bean("mysqlDs")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().url("jdbc:mysql://...").build();
}
@Bean("oracleDs")
public DataSource oracleDataSource() {
return DataSourceBuilder.create().url("jdbc:oracle:...").build();
}
}
// 服务类:按名称注入 "mysqlDs"
@Service
public class DataService {
@Resource(name = "mysqlDs") // 显式指定名称
private DataSource dataSource;
public void connect() {
System.out.println("连接到 MySQL:" + dataSource); // 输出 MySQL 数据源
}
}
2. 按类型注入(无同名 Bean 时)
当容器中只有一个匹配类型的 Bean 时,name
可留空,@Resource
自动按类型匹配:
示例:
@Service
public class UserService {
// 无 name 属性,按类型匹配唯一的光纤 UserService
@Resource
private UserService userService; // 错误!不能注入自身(循环依赖)
// 正确示例:注入同类型的另一个 Bean(假设存在)
@Resource
private UserRepository userRepository; // 假设 UserRepository 是唯一匹配的 Bean
}
3. 解决多候选 Bean 的歧义
当存在多个同类型 Bean 且未显式指定 name
时,@Resource
会抛出 NoUniqueBeanDefinitionException
。此时需通过 name
明确指定:
示例:
// 配置类:两个同类型的 PaymentService
@Configuration
public class PaymentConfig {
@Bean
public PaymentService alipayService() {
return new AlipayService();
}
@Bean
public PaymentService wechatPayService() {
return new WechatPayService();
}
}
// 服务类:未指定 name 时抛出异常(多候选)
@Service
public class OrderService {
@Resource // 错误!存在两个 PaymentService,无法确定注入哪一个
private PaymentService paymentService;
}
// 正确方式:通过 name 指定
@Service
public class OrderService {
@Resource(name = "alipayService") // 明确指定
private PaymentService paymentService;
}
4. 注入方法参数
@Resource
可标注在方法参数上,Spring 会调用该方法并注入参数:
示例:
@Service
public class LogService {
private Logger logger;
// 方法参数注入(@Resource 标注在参数上)
@Resource
public void setLogger(Logger logger) {
this.logger = logger;
}
public void log(String message) {
logger.info(message);
}
}
5. 与 @Autowired
的对比
特性 | @Resource | @Autowired |
---|---|---|
来源 | JSR-250 标准(跨框架) | Spring 自定义注解 |
匹配优先级 | 先名称,后类型 | 先类型,后名称(@Qualifier 辅助) |
可选性 | 不支持(无 required 属性) | 支持(required 默认 true ) |
多候选处理 | 必须显式 name 匹配 | 可通过 @Qualifier 或 @Primary 解决 |
适用场景 | 跨框架集成、显式名称匹配 | Spring 项目内部依赖注入 |
四、源码实现细节与关键类
1. CommonAnnotationBeanPostProcessor
Spring 处理 JSR-250 注解(如 @Resource
、@PostConstruct
)的核心类,继承自 InstantiationAwareBeanPostProcessorAdapter
。其主要方法包括:
postProcessProperties
:处理字段、方法参数的注入。postProcessBeforeInstantiation
:处理构造器注入(通过ConstructorResolver
)。
2. ResourceLoader
CommonAnnotationBeanPostProcessor
依赖 ResourceLoader
查找和加载 Bean。对于 Spring 容器,ResourceLoader
通常是 DefaultResourceLoader
,负责从 BeanFactory
中获取 Bean。
3. BeanFactory
的 getBean
方法
底层通过 BeanFactory.getBean(String name, Class<T> requiredType)
方法,根据 @Resource
的 name
和 type
属性查找匹配的 Bean。
五、注意事项与常见问题
1. 名称匹配的大小写敏感
@Resource
的 name
属性是大小写敏感的,需与 Bean 的名称完全一致(如 mysqlDs
与 MysqlDs
会被视为不同)。
2. 避免循环依赖
@Resource
无法直接解决循环依赖(与 @Autowired
类似)。若两个 Bean 相互依赖,需通过以下方式解决:
- 使用
@Lazy
延迟初始化。 - 改为构造器注入(Spring 4.3+ 支持构造器注入的循环依赖)。
3. 不支持可选依赖
@Resource
没有 required
属性,若依赖的 Bean 不存在,Spring 会直接抛出 NoSuchBeanDefinitionException
。若需可选依赖,需结合 @Autowired(required = false)
(不推荐混合使用)。
4. 与 @ComponentScan
的协同
@Resource
注入的 Bean 需被 @ComponentScan
扫描到(或通过 @Bean
显式声明),否则会因找不到 Bean 而报错。
5. 性能优化
- 避免在
@Resource
中使用复杂的名称匹配(如通配符),可能导致额外的查找开销。 - 对于高频访问的 Bean,可通过
@Primary
标记主候选,减少运行时匹配成本。
六、总结
@Resource
是 Java EE 标准的依赖注入注解,通过名称优先、类型次之的匹配规则实现依赖注入,适用于跨框架集成或需要显式指定 Bean 名称的场景。其核心机制依赖 CommonAnnotationBeanPostProcessor
和 BeanFactory
的协作,支持字段、方法参数等多种注入方式。理解其源码和使用场景,有助于开发者编写更灵活、可维护的 Spring 应用。