@Autowired and @qualifier

本文探讨了Spring框架中@Autowired注解的使用方法,特别是在候选Bean数量不是1时如何处理。介绍了如何使用@Autowired(required=false)允许自动装配失败,以及利用@Qualifier注解解决多个候选Bean时的不确定性。


当候选 Bean 数目不为 1 时的应对方法     在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验:       清单 10. 候选 Bean 数目为 0 时                      <?xml version="1.0" encoding="UTF-8" ?>   <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">          <bean class="org.springframework.beans.factory.annotation.            AutowiredAnnotationBeanPostProcessor"/>          <bean id="boss" class="com.baobaotao.Boss"/>          <!-- 将 office Bean 注释掉 -->        <!-- <bean id="office" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>        </bean>-->          <bean id="car" class="com.baobaotao.Car" scope="singleton">            <property name="brand" value=" 红旗 CA72"/>            <property name="price" value="2000"/>        </bean>   </beans>         由于 office Bean 被注释掉了,所以 Spring 容器中将没有类型为 Office 的 Bean 了,而 Boss 的 office 属性标注了 @Autowired,当启动 Spring 容器时,异常就产生了。     当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。来看一下具体的例子:       清单 11. 使用 @Autowired(required = false)                      package com.baobaotao;     import org.springframework.beans.factory.annotation.Autowired;   import org.springframework.beans.factory.annotation.Required;     public class Boss {         private Car car;       private Office office;         @Autowired      public void setCar(Car car) {           this.car = car;        }       @Autowired(required = false)       public void setOffice(Office office) {           this.office = office;        }        …   }         当然,一般情况下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到(如为了快速启动 Spring 容器,仅引入一些模块的 Spring 配置文件),所以 @Autowired(required = false) 会很少用到。     和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子:       清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean                      …   <bean id="office" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>   </bean>   <bean id="office2" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>   </bean>   …         我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean,当对 Boss 的 office 成员变量进行自动注入时,Spring 容器将无法确定到底要用哪一个 Bean,因此异常发生了。     Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了,可以通过下面的方法解决异常:       清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称                      @Autowired  public void setOffice(@Qualifier("office")Office office) {       this.office = office;   }         @Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码:     对成员变量进行注释:       清单 14. 对成员变量使用 @Qualifier 注释                      public class Boss {       @Autowired      private Car car;         @Autowired      @Qualifier("office")       private Office office;        …   }         对构造函数入参进行注释:       清单 15. 对构造函数变量使用 @Qualifier 注释                      public class Boss {       private Car car;       private Office office;         @Autowired      public Boss(Car car , @Qualifier("office")Office office){           this.car = car;           this.office = office ;        }   }         @Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。   
当候选 Bean 数目不为 1 时的应对方法     在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验:       清单 10. 候选 Bean 数目为 0 时                      <?xml version="1.0" encoding="UTF-8" ?>   <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">          <bean class="org.springframework.beans.factory.annotation.            AutowiredAnnotationBeanPostProcessor"/>          <bean id="boss" class="com.baobaotao.Boss"/>          <!-- 将 office Bean 注释掉 -->        <!-- <bean id="office" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>        </bean>-->          <bean id="car" class="com.baobaotao.Car" scope="singleton">            <property name="brand" value=" 红旗 CA72"/>            <property name="price" value="2000"/>        </bean>   </beans>         由于 office Bean 被注释掉了,所以 Spring 容器中将没有类型为 Office 的 Bean 了,而 Boss 的 office 属性标注了 @Autowired,当启动 Spring 容器时,异常就产生了。     当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。来看一下具体的例子:       清单 11. 使用 @Autowired(required = false)                      package com.baobaotao;     import org.springframework.beans.factory.annotation.Autowired;   import org.springframework.beans.factory.annotation.Required;     public class Boss {         private Car car;       private Office office;         @Autowired      public void setCar(Car car) {           this.car = car;        }       @Autowired(required = false)       public void setOffice(Office office) {           this.office = office;        }        …   }         当然,一般情况下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到(如为了快速启动 Spring 容器,仅引入一些模块的 Spring 配置文件),所以 @Autowired(required = false) 会很少用到。     和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子:       清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean                      …   <bean id="office" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>   </bean>   <bean id="office2" class="com.baobaotao.Office">        <property name="officeNo" value="001"/>   </bean>   …         我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean,当对 Boss 的 office 成员变量进行自动注入时,Spring 容器将无法确定到底要用哪一个 Bean,因此异常发生了。     Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了,可以通过下面的方法解决异常:       清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称                      @Autowired  public void setOffice(@Qualifier("office")Office office) {       this.office = office;   }         @Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码:     对成员变量进行注释:       清单 14. 对成员变量使用 @Qualifier 注释                      public class Boss {       @Autowired      private Car car;         @Autowired      @Qualifier("office")       private Office office;        …   }         对构造函数入参进行注释:       清单 15. 对构造函数变量使用 @Qualifier 注释                      public class Boss {       private Car car;       private Office office;         @Autowired      public Boss(Car car , @Qualifier("office")Office office){           this.car = car;           this.office = office ;        }   }         @Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。   
<think>嗯,用户这次想深入了解Spring框架中@Autowired和@Resource这两个注解的使用方法和区别。从用户提供的站内引用来看,已经包含了相当全面的对比信息,我需要整合这些信息并补充实际应用场景。 用户可能正在开发Spring项目时遇到了依赖注入的选择困惑。ta特意强调要了解"使用方法、区别等",说明需要实操指导而非纯理论。注意到用户引用了5个不同来源的对比,其中引用[4]的表格和代码示例特别实用,引用[5]的源码分析则适合进阶理解。 先梳理核心差异点:来源方面,@Autowired是Spring专属而@Resource是Java标准,这点在引用[1][2]都有强调。依赖查找顺序的差异是重点——@Autowired默认按类型匹配,失败时才按名称;@Resource正相反,这在引用[3][4]的代码示例中表现得特别直观。用户提供的引用[4]表格很清晰,值得保留呈现。 实际使用中要特别注意:当存在多个同类型Bean时,@Autowired必须配合@Qualifier指定名称,而@Resource可以直接用name属性指定。引用[4]的对比代码正好演示了这点。另外引用[5]提到@Autowired在构造器注入时的特殊规则:Spring4.3+版本中单构造器可省略注解,这个细节容易被忽略需要强调。 考虑设计知识结构时决定按"基础概念→使用方式→本质差异→选择策略"的递进逻辑。特别增加"典型应用场景"部分,因为用户引用的资料都偏理论,补充实际案例更有帮助。例如短信/邮件服务切换的场景,既能展示注解区别又符合实际开发需求。 最后的问题设计要侧重实践:性能影响是很多开发者忽略的,异常处理则直接关联开发体验,而现代框架整合问题正好衔接SpringBoot等热门技术。避免问"区别是什么"这类重复问题,转而引导思考使用策略。 </think>好的,我们来详细解析 `@Autowired` 和 `@Resource` 这两个在 Spring 中用于依赖注入的核心注解,涵盖它们的用法、核心区别以及相关知识点。 **核心目的:** 它们的核心目标都是实现**依赖注入 (DI)**,让 Spring 容器自动将所需的 Bean 实例赋值给标注了这些注解的字段、方法参数或构造函数参数。 --- ### 一、基础概念与来源 1. **`@Autowired` (Spring 专属)** * **来源:** 由 Spring 框架 (`org.springframework.beans.factory.annotation` 包) 定义和提供,是 Spring 生态的核心注解之一。这意味着它与 Spring 框架**强耦合**。 * **默认注入规则:** **按类型 (byType) 匹配**。Spring 容器会在其上下文中查找与需要注入的属性/参数**类型匹配**的唯一 Bean。 * 如果找到唯一匹配,注入成功。 * 如果找到多个匹配类型,Spring 会尝试**按名称 (byName) 匹配**(使用字段名/参数名作为 Bean 的 id/name 去查找)。如果按名称也找不到唯一 Bean,则会抛出 `NoUniqueBeanDefinitionException` 异常。 * **解决歧义:** * 配合 `@Qualifier("beanName")` 注解:当存在多个同类型 Bean 时,使用 `@Qualifier` 指定要注入的 Bean 的具体名称 (id/name)。 * 使用 `@Primary` 注解:在多个候选 Bean 中标记一个为首选 Bean,当按类型匹配出现多个时,优先注入被 `@Primary` 标注的 Bean。 2. **`@Resource` (Java EE 标准)** * **来源:** 由 Java EE (现 Jakarta EE) 的 **JSR-250** 规范定义 (`javax.annotation.Resource` 包,JDK 自带或需引入 `javax.annotation-api`)。它**不局限于 Spring**,理论上可在任何支持 JSR-250 的框架中使用,通用性更强。 * **默认注入规则:** * 如果指定了 `name` 属性 (`@Resource(name="myBean")`),则**严格按名称 (byName) 匹配**。 * 如果 **未指定 `name` 属性**: * 先尝试**按名称 (byName) 匹配** (使用字段名/属性名 `set` 方法对应的属性名作为 Bean 名称)。 * 如果按名称未找到,则回退到**按类型 (byType) 匹配**。 * 如果按类型匹配找到多个,且未指定 `name`,也会抛出异常。 * **解决歧义:** 主要依靠 `name` 属性显式指定 Bean 名称。它不支持 Spring 的 `@Qualifier` 或 `@Primary`(虽然在某些版本/场景下可能碰巧有效,但**不是标准行为**)。 --- ### 二、核心区别总结 | 特性 | `@Resource` | `@Autowired` | | :--------------- | :--------------------------------------------- | :---------------------------------------------- | | **所属规范/来源** | **JSR-250 (Java 标准)** `javax.annotation` | **Spring 框架专有** `org.springframework.beans.factory.annotation` | | **依赖查找顺序** | 1. 按指定名称 (`name` 属性) <br> 2. 按默认名称 (字段名/属性名) <br> 3. 按类型 | 1. **按类型** <br> 2. 按名称 (字段名/参数名) | | **解决歧义方式** | 主要依赖 `name` 属性 | 依赖 `@Qualifier("name")` 或 `@Primary` | | **是否支持构造器注入** | **不支持** (规范定义只支持字段和方法注入) | **支持** (构造器、字段、方法参数) | | **与 Spring 耦合度** | 低 (Java 标准) | 高 (Spring 特有) | | **灵活性** | 名称匹配策略清晰、直接 | 类型优先,配合 `@Qualifier`/`@Primary` 更灵活 | | **包依赖** | 通常需要引入 `javax.annotation-api` (Spring Boot 通常已包含) | 仅需 Spring 核心库 | --- ### 三、使用方法与示例 **场景:** 假设我们有一个 `NotificationService` 接口,有两个实现类 `EmailServiceImpl` 和 `SmsServiceImpl`,它们在 Spring 容器中注册的 Bean 名称分别为 `emailService` 和 `smsService`。 1. **`@Autowired` 示例 (按类型优先,需解决歧义):** ```java @Service public class MyService { // 方式1: 按类型注入 (如果 NotificationService 只有一个实现类则成功) @Autowired private NotificationService notificationService; // 方式2: 按类型 + 按名称回退成功 (字段名 notificationService 与任意Bean名都不匹配,但类型实现唯一) @Autowired private NotificationService emailService; // 字段名 emailService 匹配 Bean 名 'emailService' // 方式3: 按类型 + @Qualifier 指定名称 (解决多个同类型Bean时的首选方式) @Autowired @Qualifier("smsService") // 明确指定注入名为 'smsService' 的Bean private NotificationService commsService; // 字段名无关紧要了 // 方式4: 构造器注入 (Spring 4.3+ 单构造器可省略 @Autowired) private final NotificationService service; public MyService(@Qualifier("emailService") NotificationService service) { this.service = service; } // 方式5: Setter 方法注入 private NotificationService setterInjected; @Autowired public void setSetterInjected(@Qualifier("smsService") NotificationService service) { this.setterInjected = service; } } ``` 2. **`@Resource` 示例 (名称优先):** ```java @Service public class MyService { // 方式1: 按名称注入 (显式指定 name="emailService") @Resource(name = "emailService") private NotificationService notificationService; // 注入 emailService // 方式2: 按名称注入 (未指定name, 使用字段名 notificationService) // 如果容器中有名为 'notificationService' 的Bean则注入,否则按类型匹配 @Resource private NotificationService notificationService; // 方式3: 按名称注入 (字段名匹配 Bean 名 emailService) @Resource private NotificationService emailService; // 注入 emailService // 方式4: Setter 方法注入 (按属性名匹配) private NotificationService setterInjected; @Resource // 默认按属性名 'setterInjected' 查找Bean,找不到再按类型 public void setSetterInjected(NotificationService service) { this.setterInjected = service; } // 注意:@Resource 不支持构造器注入!以下写法是错误的或不推荐的 // @Resource // 无效或行为未定义 // public MyResourceService(NotificationService service) { ... } } ``` --- ### 四、选择建议与最佳实践 1. **优先考虑 `@Autowired`:** * 如果你的项目**深度依赖 Spring 框架**,且不需要考虑迁移到其他非 JSR-250 兼容的 DI 容器。 * 当你需要**构造器注入**(推荐用于强制依赖和不变性)时,`@Autowired` 是唯一的选择。 * 当注入逻辑更倾向于**按类型**匹配,或者你需要利用 `@Primary` 和 `@Qualifier` 提供的灵活策略来解决复杂依赖关系时。 2. **考虑使用 `@Resource`:** * 如果你希望代码**减少对 Spring 特定注解的依赖**,提高可移植性(理论上可以更容易迁移到其他支持 JSR-250 的框架)。 * 当你对依赖项的注入有**明确的 Bean 名称要求**,并且希望直接通过 `name` 属性清晰地表达意图时。`@Resource(name="exactBeanName")` 非常直观。 * 在简单的按名称注入场景下,代码可能看起来更简洁(省略 `@Qualifier`)。 3. **一致性:** 在一个项目中,尽量统一使用一种注解风格(`@Autowired` 或 `@Resource`),以保持代码整洁和可维护性。Spring Boot 默认引导的项目通常大量使用 `@Autowired`。 4. **构造器注入优先:** 对于强制依赖项,**强烈推荐使用 `@Autowired` 标注的构造器注入**(或在 Spring 4.3+ 单构造器时可省略)。这能保证依赖在对象创建时完全初始化,使 Bean 处于有效状态,并有利于声明不可变字段,提高线程安全性和可测试性[^5]。 --- ### 五、典型应用场景 1. **服务层注入数据访问层 (DAO/Repository):** ```java @Service public class UserServiceImpl implements UserService { // 使用 @Autowired 或 @Resource 注入 UserRepository private final UserRepository userRepository; @Autowired // 推荐构造器注入 public UserServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; } // ... 业务方法使用 userRepository } ``` 2. **控制器 (Controller) 注入服务层:** ```java @RestController @RequestMapping("/api/users") public class UserController { @Autowired // 或 @Resource private UserService userService; // ... 请求处理方法使用 userService } ``` 3. **配置类中注入 Bean 或 Environment:** ```java @Configuration public class AppConfig { @Autowired // 常用于注入环境变量或其他配置Bean private Environment env; // ... 使用 env 配置其他Bean } ``` 4. **动态选择实现 (如切换 Email/SMS 服务):** ```java @Service public class NotificationSender { // 使用 @Qualifier 或 @Resource(name=...) 根据配置动态注入不同实现 @Autowired @Qualifier("${notification.service.type}") // 假设配置了 notification.service.type=emailService private NotificationService notificationService; public void send(String message) { notificationService.send(message); } } ``` --- ### 六、相关问题 (Related Questions) 1. **除了 `@Autowired` 和 `@Resource`,Spring 还支持哪些依赖注入方式?`@Inject` 是什么?它与 `@Autowired`的区别是什么?**[^3] 2. **构造器注入、Setter 注入和字段注入 (`@Autowired`/`@Resource` 在字段上) 各有什么优缺点?为什么推荐构造器注入?**[^5] 3. **`@Primary` 注解是如何工作的?在哪些场景下使用它比 `@Qualifier` 更方便?** 4. **当 `@Autowired` 注入失败(如 `NoSuchBeanDefinitionException`, `NoUniqueBeanDefinitionException`)时,如何进行排查和修复?** 5. **在 Spring Boot 应用中,`@Autowired` 和 `@Resource` 的性能有显著差异吗?选择哪个对应用启动或运行效率影响更大?** 6. **如何将 `@Resource` 与 Jakarta CDI (Contexts and Dependency Injection) 规范一起使用?这与在 Spring 中使用有何异同?** [^1]: 来源说明 `@Autowired` 与 `@Resource` 的来源差异和查找顺序根本不同。 [^2]: 强调了两者来源的根本区别(Spring专有 vs Java标准/JSR-250)。 [^3]: 提及了 `@Resource` 的名称匹配特性,并引出了 `@Inject` 作为对比点。 [^4]: 清晰的代码示例对比了 `@Resource(name=...)` 和 `@Autowired` + `@Qualifier` 的等效写法,并提供了特性对比表格的核心框架。 [^5]: 源码分析结论指出 `@Autowired` 支持构造器注入及其省略规则,这是 `@Resource` 不具备的关键能力,并强调了构造器注入的优势。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值