当前位置: 首页 > ds >正文

@Qualifier(“beanName“) 详解

@Qualifier("beanName") 是什么?

@Qualifier 注解,正如其名(Qualifier 意为“限定符”、“修饰符”),它的核心作用是在依赖注入时,提供更精确的筛选条件,帮助 Spring 从多个同类型的候选 Bean 中,准确地“限定”出你想要的那一个。

@Primary 是在众多选项中选出一个“默认冠军”,而 @Qualifier 则像是直接通过“身份证号”(Bean 的名字)来点名。

如何使用 @Qualifier

继续我们之前的 NotificationService 例子。我们有两个 Bean:EmailNotificationServiceSmsNotificationService

第一步:为 Bean 命名

Spring 默认会给 Bean 一个名字,通常是类名首字母小写(如 emailNotificationService)。但为了更清晰,我们最好自己指定一个简短的名字。这可以通过 @Service("beanName")@Component("beanName") 来实现。

import org.springframework.stereotype.Service;@Service("emailNotifier") // 给这个Bean起一个限定名 "emailNotifier"
public class EmailNotificationService implements NotificationService {// ...
}
import org.springframework.stereotype.Service;@Service("smsNotifier") // 给这个Bean起一个限定名 "smsNotifier"
public class SmsNotificationService implements NotificationService {// ...
}

现在,IoC 容器中有两个 NotificationService 类型的 Bean,它们分别被命名为 emailNotifiersmsNotifier

第二步:在注入点使用 @Qualifier 进行点名

现在,OrderService 可以明确地告诉 Spring 它想要哪一个实现。

场景A:需要邮件通知

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service
public class OrderService {private final NotificationService notificationService;@Autowiredpublic OrderService(@Qualifier("emailNotifier") NotificationService notificationService) {// @Qualifier说:在所有NotificationService类型的Bean中,// 我就要那个名字叫 "emailNotifier" 的!this.notificationService = notificationService;}// ...
}

场景B:另一个服务,需要短信通知

@Service
public class UrgentOrderService {private final NotificationService notificationService;@Autowiredpublic UrgentOrderService(@Qualifier("smsNotifier") NotificationService notificationService) {// 这里明确指定了需要短信通知服务this.notificationService = notificationService;}// ...
}

通过这种方式,@Qualifier 提供了极高的灵活性。不同的消费方可以根据自己的需求,注入同一个接口的不同实现,而无需修改 Bean 定义方的代码。


正面对决:@Primary vs @Qualifier,谁的优先级更高?

答案非常明确:@Qualifier 的优先级更高。

可以这样理解它们的逻辑关系:

  • Spring 的注入逻辑
    1. 首先按类型查找:找到所有类型为 NotificationService 的 Bean。
    2. 发现多个候选者emailNotificationServicesmsNotificationService
    3. 检查是否有 @Qualifier 精确指定
      • 如果有(比如 @Qualifier("smsNotifier")),Spring 就会忽略其他所有因素(包括 @Primary),直接选择名为 smsNotifier 的 Bean。@Qualifier 的指令是绝对的。
      • 如果没有 @Qualifier,Spring 才会继续下一步。
    4. 检查是否有 @Primary 默认选项
      • 在剩下的候选者中,寻找被 @Primary 标记的 Bean。
      • 如果找到了,就注入那个 @Primary 的 Bean。
      • 如果没有找到 @Primary 的 Bean(也没有 @Qualifier),Spring 就会再次陷入困惑,抛出 NoUniqueBeanDefinitionException

我们用一个比喻来描述:

你去一家快餐店点汉堡。

  • @Primary:菜单上,“招牌牛肉汉堡”旁边印着一个大大的 “本店推荐” 标志。
  • @Qualifier:你对服务员说的话。

场景1:只使用 @Primary

  • 你对服务员说:“来个汉堡。”(相当于 @Autowired NotificationService service;
  • 服务员看到菜单上的“本店推荐”,就直接给你拿了“招牌牛肉汉堡”。

场景2:@Primary@Qualifier 同时存在

  • 菜单上“招牌牛肉汉堡”依然是“本店推荐”(@Primary)。
  • 但你对服务员说:“我不要推荐的,给我来一个‘香辣鸡腿堡’。”(相当于 @Autowired @Qualifier("spicyChickenBurger") Burger burger;
  • 服务员会听你的话,而不是看菜单的推荐。你的直接指令(@Qualifier)优先级更高。

代码示例验证优先级

@Service("emailNotifier")
@Primary // Email服务是首选
public class EmailNotificationService implements NotificationService { ... }@Service("smsNotifier")
public class SmsNotificationService implements NotificationService { ... }// --- 消费者 ---
@Service
public class OrderService {private final NotificationService notificationService;@Autowiredpublic OrderService(@Qualifier("smsNotifier") NotificationService notificationService) {// 尽管Email是@Primary,但这里通过@Qualifier明确指定了smsNotifierthis.notificationService = notificationService;}public void placeOrder() {// 这将调用 SmsNotificationServicenotificationService.sendNotification("您的订单已成功创建!");}
}

在这个例子中,最终被注入到 OrderService 的是 SmsNotificationService,完美验证了 @Qualifier 的更高优先级。

总结

特性@Primary@Qualifier("beanName")
角色设置全局默认值进行局部精确指定
决定权Bean 的提供方Bean 的消费方
优先级较低较高
使用时机当多个实现中有一个是绝大多数情况下的选择时。当需要灵活地在多个实现之间切换,或者需要覆盖 @Primary 的默认行为时。

@Primary@Qualifier 是 Spring IoC 中一对相辅相成的利器,共同解决了依赖注入中的歧义性问题,使得代码既能保持简洁(依赖于默认),又能拥有足够的灵活性(按需指定)。

http://www.xdnf.cn/news/15859.html

相关文章:

  • 一个逻辑问题
  • 《设计模式之禅》笔记摘录 - 8.命令模式
  • Day06_C语言网络编程20250718mobus重点
  • gin数据解析和绑定
  • 门控线性单元GLU (Gated Linear Unit)
  • Go语言流程控制(if / for)
  • 一小时学习Redis
  • websocket案例 599足球比分
  • 海森矩阵(Hessian Matrix)在SLAM图优化和点云配准中的应用介绍
  • 实战指南|智慧无人机安防系统搭建全流程解析
  • 深入理解Linux文件操作:stdin/stdout/stderr与C语言文件函数全解析
  • PDF 拆分合并PDFSam:开源免费 多文件合并 + 按页码拆分 本地处理
  • 突破性量子芯片问世:电子与光子首次集成,开启量子技术规模化应用新篇章
  • 暑期自学嵌入式——Day05补充(C语言阶段)
  • 二分答案之第 K 小/大
  • Visual Studio编译WPF项目生成的文件介绍
  • 服务器mysql数据的简单备份脚本
  • 二、Dify 版本升级教程(LInux-openeuler)
  • iOS OC 图片压缩
  • vue2 面试题及详细答案150道(101 - 120)
  • 国产替代:ASP4644在电信通信设备中的测试与应用前景
  • Java类:BigDecimal 的用法:乘法
  • IDEA 2020.1版本起下载JDK
  • Logback 配置的利器:深入理解<property>与<variable>
  • vue2 面试题及详细答案150道(21 - 40)
  • 闭包的定义和应用场景
  • Rust实战:高效对接Postman API
  • Spring中的SpEL是什么
  • Springboot3整合Elasticsearch8(elasticsearch-java)
  • [2025CVPR-目标检测方向]FSHNet:一种用于3D物体检测的全稀疏混合网络。