
Autowried和Resource注解总结
Autowried和Resource注解总结
1、掌握@Autowried和@Resource
在Spring框架中,@Autowired
和@Resource
都是用于实现依赖注入(Dependency Injection, DI)的核心注解,它们可以自动将所需的Bean装配到目标组件中。尽管功能相似,但它们在使用方法、来源和注入机制上存在显著差异。
@Autowired 的使用方法
@Autowired
是 Spring 框架提供的注解,它默认按照类型 (byType) 在应用上下文中查找匹配的Bean进行注入。
基本用法
@Autowired
可以标注在构造方法、成员变量、Setter方法或任意配置方法上,以实现自动装配。
-
属性注入(Field Injection):这是最简洁和常见的使用方式。
@Component public class MyService { @Autowired private MyRepository myRepository; // ... }
-
构造方法注入(Constructor Injection):Spring团队推荐使用此方式,因为它可以确保依赖在对象实例化时就完全可用,并且可以轻松地将依赖声明为
final
。@Component public class MyService { private final MyRepository myRepository; @Autowired public MyService(MyRepository myRepository) { this.myRepository = myRepository; } }
注意:从 Spring 4.3 版本开始,如果类中只有一个构造方法,
@Autowired
注解可以省略。 -
Setter方法注入(Setter Injection):通过Setter方法进行注入。
@Component public class MyService { private MyRepository myRepository; @Autowired public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository; } }
处理多个同类型Bean
当IOC容器中存在多个相同类型的Bean时,仅使用@Autowired
会引发歧义,导致注入失败。此时可以结合@Qualifier
注解,通过指定Bean的名称来精确注入。
public interface MessageService {
String sendMessage(String message);
}
@Component("emailService")
public class EmailService implements MessageService {
// ...
}
@Component("smsService")
public class SmsService implements MessageService {
// ...
}
@Component
public class MessageProcessor {
@Autowired
@Qualifier("smsService")
private MessageService messageService;
}
@Resource 的使用方法
@Resource
是 Java EE 的规范之一(JSR-250),并非Spring独有,但Spring提供了对它的支持。 它默认按照名称 (byName) 进行Bean的查找和注入。
注入顺序
@Resource
的注入顺序遵循以下规则:
- 按名称查找:首先会尝试使用指定的
name
属性去匹配Bean的ID。如果没有指定name
属性,它会默认使用被注解的字段或方法名作为Bean的名称进行查找。 - 按类型查找:如果按名称无法找到匹配的Bean,
@Resource
会回退到按类型进行查找。 - 使用
@Qualifier
:如果按类型查找到多个候选Bean,它会进一步尝试匹配@Qualifier
指定的名称。
如果最终找不到唯一匹配的Bean,程序会抛出异常。
基本用法
@Resource
通常标注在成员变量或Setter方法上。它不支持构造方法注入。
-
属性注入:
- 不指定名称:默认使用字段名
myRepository
作为Bean的名称进行注入。@Component public class MyService { @Resource private MyRepository myRepository; }
- 指定名称:通过
name
属性明确指定要注入的Bean的ID。@Component public class MessageProcessor { @Resource(name = "emailService") private MessageService messageService; }
- 不指定名称:默认使用字段名
-
Setter方法注入:
@Component public class MyService { private MyRepository myRepository; @Resource public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository; } }
@Autowired 与 @Resource 的核心区别
特性 | @Autowired | @Resource |
---|---|---|
来源 | Spring 框架 | Java EE (JSR-250 规范) |
默认注入规则 | 按类型 (byType) | 按名称 (byName) |
注入顺序 | 1. 按类型查找。 2. 如果有多个匹配,再按变量名查找。 3. 可使用 @Qualifier 指定名称。 |
1. 按name 属性查找。2. 若无 name 属性,按字段/方法名查找。3. 若按名称找不到,则回退到按类型查找。 |
支持的注入位置 | 构造方法、成员变量、Setter方法、配置方法、参数 | 成员变量、Setter方法、类 |
主要属性 | required (boolean类型,默认为 true ,表示是否必须注入成功) |
name (String类型,指定Bean的名称)type (Class类型,指定Bean的类型) |
兼容性 | 仅适用于Spring环境。 | 遵循Java标准,可在支持JSR-250的框架中使用,如Java EE环境。 |
总结来说,@Autowired
和 @Resource
都是实现依赖注入的强大工具。@Autowired
是Spring生态中的首选,特别是当结合构造方法注入时,能更好地保证代码的健壮性。而 @Resource
提供了更强的语义(按名称获取资源),并且作为Java标准,具有更好的跨框架兼容性。
2、@Autowried和@Resource底层注入步骤
在 Spring 6 中,@Autowired
和 @Resource
的底层注入步骤都深度集成在 Spring Bean 的生命周期中。这个过程的核心是 BeanPostProcessor
接口,它允许在 Bean 初始化前后插入自定义逻辑。
@Autowired
和 @Resource
分别由两个不同的 BeanPostProcessor
实现来处理:
@Autowired
由AutowiredAnnotationBeanPostProcessor
处理。@Resource
由CommonAnnotationBeanPostProcessor
处理。
尽管最终都实现了依赖注入,但它们的底层步骤和解析逻辑有所不同。
统一前置步骤:Bean生命周期
无论是哪个注解,注入都发生在 Bean 生命周期的特定阶段。一个简化的流程如下:
- 实例化 (Instantiation): Spring 容器通过构造函数或工厂方法创建 Bean 的一个原始实例。此时,Bean 仅仅是一个普通的 Java 对象,依赖项都还是
null
。 - 填充属性 (Populate Properties): 这是依赖注入发生的关键阶段。Spring 容器会调用相关的
BeanPostProcessor
(如AutowiredAnnotationBeanPostProcessor
)来扫描 Bean,查找被@Autowired
或@Resource
等注解标记的字段和方法,并从容器中查找、解析并注入所需的依赖。 - 初始化 (Initialization): 在属性填充完毕后,Spring 会执行进一步的初始化操作,例如调用
@PostConstruct
注解的方法或afterPropertiesSet
方法。
接下来,我们将深入探讨在“填充属性”阶段,这两个注解各自的底层注入步骤。
@Autowired 的底层注入步骤
@Autowired
的处理由 AutowiredAnnotationBeanPostProcessor
负责,它是一个 InstantiationAwareBeanPostProcessor
,这意味着它可以在实例化后、属性填充前介入。
其核心步骤如下:
-
寻找注入点:
在 Bean 实例化之后,AutowiredAnnotationBeanPostProcessor
的postProcessProperties
方法会被调用。此方法会扫描当前 Bean 的所有字段、方法和构造函数,查找被@Autowired
注解标记的“注入点”(Injection Points)。找到的注入元数据(如需要注入的类型、@Qualifier
信息等)会被缓存起来,以提高后续处理效率。 -
解析依赖描述符:
对于每一个注入点,Spring 会创建一个“依赖描述符”(DependencyDescriptor
)。这个描述符包含了注入所需的所有上下文信息,例如:- 需要注入的 Bean 的类型。
- 字段或参数的名称。
- 是否是必需的 (
required
属性)。 - 是否存在
@Qualifier
或其他限定符注解。
-
在容器中查找候选 Bean:
Spring 容器(BeanFactory
)会使用这个依赖描述符来查找匹配的候选 Bean。这个查找过程遵循@Autowired
的核心逻辑:- 按类型查找 (byType): 容器会首先根据依赖描述符中指定的类型,在自身以及父容器中查找所有匹配该类型的 Bean。
- 处理多候选 Bean:
- 如果只找到一个唯一的 Bean,那么它就是注入的目标,流程继续。
- 如果找到了多个同类型的 Bean,Spring 会启动一个消歧义(disambiguation)流程:
@Primary
注解: 检查是否有候选 Bean 被标记为@Primary
。如果有,则优先选择它。- 变量名匹配: 如果没有
@Primary
Bean,Spring 会将注入点的变量名或参数名作为 Bean 的名称,在候选者中进行匹配。如果匹配成功,则选择该 Bean。 @Qualifier
注解: 如果注入点上同时使用了@Qualifier("beanName")
,Spring 会直接根据beanName
在所有候选 Bean 中精确查找。
-
注入依赖:
一旦找到了唯一的候选 Bean,Spring 就会通过反射机制将其注入到目标位置。- 对于字段注入,使用
Field.set()
方法。 - 对于 Setter 方法注入,调用该 Setter 方法并传入依赖 Bean。
- 对于字段注入,使用
-
处理注入失败:
如果在整个查找过程中没有找到任何匹配的 Bean,并且@Autowired
的required
属性为true
(默认值),Spring 将会抛出NoSuchBeanDefinitionException
或UnsatisfiedDependencyException
异常,导致应用启动失败。如果required
为false
,则会跳过注入,该依赖保持为null
。
@Resource 的底层注入步骤
@Resource
的处理由 CommonAnnotationBeanPostProcessor
负责,它同样也是一个 InstantiationAwareBeanPostProcessor
。值得注意的是,在 Spring 6 中,@Resource
对应的是 jakarta.annotation.Resource
包路径,以兼容 Jakarta EE 9+ 规范。
其注入步骤如下:
-
寻找注入点:
与@Autowired
类似,CommonAnnotationBeanPostProcessor
也会在 Bean 实例化后扫描类,查找被@Resource
注解标记的字段和方法,并缓存这些元数据。 -
确定要查找的 Bean 名称:
@Resource
的核心是“按名称查找”。 它确定 Bean 名称的顺序是:- 检查
name
属性: 首先,检查@Resource(name = "beanName")
中是否明确指定了name
属性。如果指定了,就使用这个名称。 - 使用字段名/属性名: 如果没有指定
name
属性,Spring 会默认使用被注解的字段名或 Setter 方法对应的属性名作为要查找的 Bean 名称。 例如,@Resource private MyService myService;
会查找名为 “myService” 的 Bean。
- 检查
-
按名称在容器中查找:
使用上一步确定的名称,直接在 Spring 容器中查找同名的 Bean。- 如果找到了一个名称匹配的 Bean,Spring 会检查其类型是否与注入点的类型兼容。如果兼容,则注入成功,流程结束。
- 如果按名称没有找到 Bean,则会进入回退策略。
-
回退策略 (Fallback):
这是@Resource
与@Autowired
行为相似的地方。当按名称查找失败时,@Resource
会回退到按类型查找。- 它会查找容器中与注入点类型唯一匹配的 Bean。
- 如果按类型找到了唯一的 Bean,则注入该 Bean。
- 如果按类型找到了多个候选 Bean,此时它会尝试使用字段名或属性名作为限定符再次进行匹配(类似于
@Autowired
的消歧义流程)。
-
注入依赖:
找到唯一的 Bean 后,通过反射将其注入到目标字段或方法中。 -
处理注入失败:
如果在所有步骤(按名称查找和按类型回退)之后,仍然无法找到一个唯一的、符合条件的 Bean,Spring 将会抛出异常,导致依赖注入失败。
3、@Autowried和@Resource和解决依赖注入冲突
在Spring框架中,当进行依赖注入时,如果容器中存在多个类型相同的Bean,就会产生注入冲突或歧义性(Ambiguity)。@Autowired
和@Resource
本身提供了解决冲突的机制,此外Spring还支持其他几种方式来精确控制注入过程。
@Autowired 解决依赖注入冲突的方式
@Autowired
默认按类型(byType)注入。当发现多个相同类型的Bean时,它会按照以下顺序尝试解决冲突:
1. 使用 @Primary
注解
@Primary
注解可以被添加到Bean的定义上,用来标识在多个候选Bean中,哪一个应该是首选的。当@Autowired
遇到多个同类型的Bean时,它会优先选择被@Primary
标注的那个。
示例:
假设我们有一个MessageService
接口和两个实现类。
public interface MessageService {
String send();
}
@Component("emailService")
public class EmailServiceImpl implements MessageService {
// ...
}
@Primary
@Component("smsService")
public class SmsServiceImpl implements MessageService {
// ...
}
在注入时,smsService
将会被自动注入,因为它被标记为@Primary
。
@Component
public class NotificationManager {
// 这里会自动注入 SmsServiceImpl 的实例
@Autowired
private MessageService messageService;
}
这种方式的优点是提供了一个全局的默认选项,但如果需要注入非首选的Bean,则需要其他方式。
2. 使用 @Qualifier
注解
@Qualifier
注解与@Autowired
配合使用,通过指定Bean的名称来精确选择要注入的Bean。这解决了@Primary
只能有一个首选的问题,允许在不同的注入点选择不同的实现。
示例:
继续上面的例子,我们可以按名称注入emailService
。
@Component
public class NotificationManager {
// 通过 @Qualifier 指定注入 id 为 "emailService" 的 Bean
@Autowired
@Qualifier("emailService")
private MessageService messageService;
}
@Qualifier
提供了最灵活、最精确的控制,是解决@Autowired
注入冲突最常用的方法。
3. 变量名匹配
如果既没有使用@Primary
也没有使用@Qualifier
,@Autowired
会尝试将注入点的变量名作为Bean的ID在候选者中进行匹配。
示例:
如果变量名与某个Bean的ID(默认为类名首字母小写)一致,Spring会选择该Bean。
@Component
public class NotificationManager {
// 变量名 "smsService" 匹配到了 Bean "smsService"
// 因此会注入 SmsServiceImpl 的实例
@Autowired
private MessageService smsService;
}
这种方式依赖于代码命名规范,可读性较好,但在某些情况下可能不够明确。
@Resource 解决依赖注入冲突的方式
@Resource
默认按名称(byName)注入,因此它在设计上就天然地倾向于避免类型冲突。它的解析顺序本身就是一种解决冲突的机制。
1. 使用 name
属性
@Resource
最直接的解决冲突方式是使用其name
属性,直接指定要注入的Bean的ID。
示例:
@Component
public class NotificationManager {
// 直接指定注入 id 为 "smsService" 的 Bean
@Resource(name = "smsService")
private MessageService messageService;
}```
这是`@Resource`最推荐和最明确的使用方式。
#### 2. 使用字段名/方法名匹配
如果`@Resource`没有指定`name`属性,它会默认使用被注解的字段名或方法对应的属性名作为Bean的名称进行查找。
**示例:**
```java
@Component
public class NotificationManager {
// 字段名 "emailService" 将被用作 Bean 的名称进行查找
@Resource
private MessageService emailService;
}
这与@Autowired
的变量名匹配机制非常相似,也是@Resource
默认的核心工作模式。
3. 回退到按类型查找
只有当按名称完全找不到匹配的Bean时,@Resource
才会回退到按类型查找。如果此时按类型找到了唯一的Bean,则注入成功。但如果按类型依然找到多个候选者,它会抛出NoUniqueBeanDefinitionException
异常,因为它无法解决这种二级冲突。
通用的其他解决冲突方式
除了上述注解自带的功能外,还可以使用更通用的配置方式来处理注入冲突。
使用Java配置类中的@Qualifier
在通过@Bean
方法定义Bean时,也可以结合@Qualifier
来创建具有特定限定符的Bean。
@Configuration
public class AppConfig {
@Bean
@Qualifier("main")
public MyService mainService() {
return new MyServiceImplA();
}
@Bean
@Qualifier("secondary")
public MyService secondaryService() {
return new MyServiceImplB();
}
}
在注入时,可以使用@Qualifier("main")
或@Qualifier("secondary")
来选择。
使用泛型作为限定符
如果你的Bean是泛型类型,Spring 6在处理泛型注入时更加智能,可以将具体的泛型类型作为一种限定条件。
示例:
// 定义泛型接口
public interface Storage<T> {
void save(T data);
}
// 字符串存储实现
@Component
public class StringStorage implements Storage<String> {
// ...
}
// 整数存储实现
@Component
public class IntegerStorage implements Storage<Integer> {
// ...
}
// 注入时,Spring会根据泛型类型进行匹配
@Component
public class DataProcessor {
// Spring能识别出这里需要 Storage<String> 类型的Bean
@Autowired
private Storage<String> stringStorage;
}
这种方式在处理具有层级或特定数据类型的服务时非常有用,代码也更加类型安全和清晰。
4、@Autowried和@Resource注解区别
@Autowired
和 @Resource
注解区别的总结。
核心区别对比
特性 | @Autowired | @Resource |
---|---|---|
来源 | Spring 框架自带的注解。 | Java 的标准规范 (JSR-250),Spring 框架提供了对它的支持。在 Spring 6/Spring Boot 3 中,它来自 jakarta.annotation 包。 |
默认注入规则 | 按类型 (byType)。首先在 Spring 容器中查找与注入点类型匹配的 Bean。 | 按名称 (byName)。首先尝试根据名称在 Spring 容器中查找 Bean。 |
注入顺序与流程 | 1. 按类型查找匹配的 Bean。 2. 如果找到多个,则尝试将变量名作为 Bean ID 进行匹配。 3. 如果仍有歧义,需要配合 @Qualifier 或 @Primary 来精确指定。 |
1. 首先检查 name 属性,使用其值作为 Bean ID 查找。2. 如果 name 属性未指定,则使用字段名或方法名作为 Bean ID 查找。3. 如果按名称完全找不到,则回退到按类型查找。 |
解决冲突方式 | 依赖 @Qualifier("beanName") 来指定名称,或依赖 @Primary 标注首选 Bean。 |
主要通过 name 属性 (@Resource(name = "beanName") ) 来直接指定名称。 |
支持的注入位置 | 构造方法、成员变量、Setter 方法、任意配置方法。Spring 官方推荐使用构造方法注入。 | 成员变量、Setter 方法。不支持构造方法注入。 |
主要属性 | required (boolean):默认为 true ,表示依赖必须存在,否则抛出异常。 |
name (String):用于显式指定要注入的 Bean 的名称。type (Class):用于指定要注入的 Bean 的类型(较少使用)。 |
代码耦合度 | 与 Spring 框架耦合度较高。如果更换为其他 DI 框架,此注解将失效。 | 与 Java 标准耦合,与具体框架解耦。在其他支持 JSR-250 规范的框架(如 Java EE 容器)中也能使用,移植性更好。 |
总结与选择建议
-
优先选择
@Autowired
:在纯粹的 Spring 应用中,@Autowired
是首选。它与 Spring 生态(如@Primary
、构造函数注入)的集成度更高,特别是构造函数注入方式,可以保证依赖的不可变性,是 Spring 官方推荐的最佳实践。 -
何时使用
@Resource
:当你希望代码有更好的移植性,或者想非常明确地通过名称来注入资源时,@Resource
是一个很好的选择。它的(name = "...")
语法在语义上非常清晰,就是“我要获取名为 xxx 的资源”。
简单来说,可以这样记忆:
@Autowired
:Spring 的“亲儿子”,默认按类型,功能更丰富,首选构造注入。@Resource
:Java 的“标准兵”,默认按名称,语义更明确,兼容性更强。