Java-Spring 依赖注入详解--多个类实现与选择 - 若

news/2025/12/31 16:40:31/文章来源:https://www.cnblogs.com/zhanchenjin/p/19401553

多个接口实现的解决方案 - 实战示例

🤔 问题场景

假设你有一个 NotificationService 接口,有两个实现类:

// 接口
public interface NotificationService {void send(String message);
}// 实现1:发邮件
@Component
public class EmailNotificationService implements NotificationService {@Overridepublic void send(String message) {System.out.println("发送邮件: " + message);}
}// 实现2:发短信
@Component
public class SmsNotificationService implements NotificationService {@Overridepublic void send(String message) {System.out.println("发送短信: " + message);}
}

现在你想在某个类中使用 NotificationService

@Service
public class OrderService {@Autowiredprivate NotificationService notificationService;  // ❌ 错误!Spring 不知道用哪个
}

Spring 会报错:

NoUniqueBeanDefinitionException: No qualifying bean of type 'NotificationService' available: 
expected single matching bean but found 2: emailNotificationService, smsNotificationService

✅ 解决方案

方案1:使用 @Qualifier(明确指定)

使用场景: 你需要明确知道用哪个实现

// 实现类(可以指定 Bean 名称)
@Component("emailService")  // 自定义名称,不写的话默认是 emailNotificationService
public class EmailNotificationService implements NotificationService {// ...
}@Component("smsService")
public class SmsNotificationService implements NotificationService {// ...
}// 使用时指定
@Service
public class OrderService {@Autowired@Qualifier("emailService")  // 👈 明确指定用 emailServiceprivate NotificationService notificationService;public void createOrder() {notificationService.send("订单创建成功");  // 会调用 EmailNotificationService}
}

FastBee 项目中的实际例子:

// SipCmdImpl.java
@Autowired
@Qualifier(value = "udpSipServer")  // 明确指定要用 "udpSipServer" 这个 Bean
private SipProvider sipserver;

方案2:使用 @Primary(设置默认)

使用场景: 有一个是默认实现,大部分情况都用它

@Component
@Primary  // 👈 标记为默认实现
public class EmailNotificationService implements NotificationService {// ...
}@Component
public class SmsNotificationService implements NotificationService {// ...
}// 使用时不需要指定,自动用 @Primary 标记的
@Service
public class OrderService {@Autowiredprivate NotificationService notificationService;  // ✅ 会自动用 EmailNotificationServicepublic void createOrder() {notificationService.send("订单创建成功");}
}// 如果某个地方需要明确用 SMS,可以配合 @Qualifier
@Service
public class AlertService {@Autowired@Qualifier("smsNotificationService")  // 明确用 SMSprivate NotificationService notificationService;
}

FastBee 项目中的实际例子:

// DruidConfig.java
@Bean(name = "dynamicDataSource")
@Primary  // 标记为主要数据源,默认都用这个
public DynamicDataSource dataSource(DataSource masterDataSource) {// ...
}

方案3:注入所有实现(List/Map)

使用场景: 你需要使用所有的实现,比如广播消息

使用 List

@Service
public class BroadcastService {@Autowiredprivate List<NotificationService> notificationServices;  // 👈 注入所有实现public void broadcast(String message) {// 遍历所有实现,都发送一遍for (NotificationService service : notificationServices) {service.send(message);}// 结果:// 发送邮件: 消息内容// 发送短信: 消息内容}
}

使用 Map(可以按名称获取)

@Service
public class NotificationManager {@Autowiredprivate Map<String, NotificationService> notificationServiceMap;  // Map 包含:// "emailNotificationService" -> EmailNotificationService 实例// "smsNotificationService" -> SmsNotificationService 实例public void sendByType(String type, String message) {NotificationService service = notificationServiceMap.get(type);if (service != null) {service.send(message);}}// 使用// sendByType("emailNotificationService", "消息");  // 发邮件// sendByType("smsNotificationService", "消息");    // 发短信
}

方案4:使用条件注解(根据配置选择)

使用场景: 根据配置文件决定用哪个实现

// 生产环境用邮件
@Component
@ConditionalOnProperty(name = "notification.type", havingValue = "email")
public class EmailNotificationService implements NotificationService {// ...
}// 测试环境用短信
@Component
@ConditionalOnProperty(name = "notification.type", havingValue = "sms")
public class SmsNotificationService implements NotificationService {// ...
}

配置文件 application.yml:

notification:type: email  # 只有 EmailNotificationService 会被创建

📊 方案对比表

方案 什么时候用 优点 缺点
@Qualifier 需要明确指定用哪个 最灵活,清晰明确 需要记住 Bean 名称
@Primary 有一个默认实现 简单,不需要指定 不够灵活,可能混淆
List/Map 需要所有实现 可以统一处理 不适合只用一个的情况
条件注解 根据配置选择 灵活切换环境 配置较复杂

💡 推荐使用建议

场景1:开发和生产用不同的实现

// 推荐:使用 @Primary + @Qualifier 组合
@Component
@Primary
public class EmailNotificationService implements NotificationService { }@Component
@ConditionalOnProperty(name = "env", havingValue = "test")
public class MockNotificationService implements NotificationService { }

场景2:大部分地方用默认,少数地方用特殊的

// 推荐:使用 @Primary
@Component
@Primary  // 默认用这个
public class EmailNotificationService implements NotificationService { }@Component
public class SmsNotificationService implements NotificationService { }// 默认用 Email
@Autowired
private NotificationService service;  // 特殊地方用 SMS
@Autowired
@Qualifier("smsNotificationService")
private NotificationService smsService;

场景3:需要发送到多个渠道

// 推荐:使用 List
@Autowired
private List<NotificationService> services;public void notifyAll(String message) {services.forEach(service -> service.send(message));
}

🎯 记忆口诀

一个接口多个实现,Spring 不知道用哪个

  • 要明确指定 → 用 @Qualifier
  • 有默认首选 → 用 @Primary
  • 全都要用 → 用 ListMap
  • 按配置选 → 用条件注解

🔍 FastBee 项目中的真实案例

案例1:使用 @Qualifier 指定 Bean

// SipCmdImpl.java
@Autowired
@Qualifier(value = "udpSipServer")  // 明确指定要用名为 "udpSipServer" 的 Bean
private SipProvider sipserver;

案例2:使用 @Primary 设置默认

// DruidConfig.java
@Bean(name = "dynamicDataSource")
@Primary  // 标记为主要数据源
public DynamicDataSource dataSource(DataSource masterDataSource) {// ...
}

案例3:多个实现类通过 Map 管理

在 FastBee 项目中,多个 IReqHandler 实现类(如 RegisterReqHandlerInviteReqHandler 等)通过手动注册到 Map 中管理:

// GBListenerImpl.java
private static final Map<String, IReqHandler> requestProcessorMap = new ConcurrentHashMap<>();public void addRequestProcessor(String method, IReqHandler processor) {requestProcessorMap.put(method, processor);  // 根据 method 选择不同的处理器
}

希望这个例子能帮你理解! 🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1069293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【图像处理基石】什么是神经渲染?

前言 在计算机图形学和计算机视觉的交叉领域&#xff0c;神经渲染&#xff08;Neural Rendering&#xff09;正成为最热门的研究方向之一。它打破了传统渲染依赖手工设计规则的局限&#xff0c;用神经网络学习从数据到图像的映射&#xff0c;让“AI画画”“数字人重建”“场景生…

centos7配置yum软件源

你遇到的错误是由于 CentOS 7 的官方软件源已经停止维护,导致无法解析 mirrorlist.centos.org,从而 yum 无法获取软件包列表。📌 背景说明:CentOS 7 已于 2024 年 6 月 30 日正式 EOL(End Of Life)。 官方不再提…

2025年西安电子科技大学计算机考研复试机试真题(附 AC 代码 + 解题思路)

2025年西安电子科技大学计算机考研复试机试真题 2025年西安电子科技大学计算机考研复试上机真题 历年西安电子科技大学计算机考研复试上机真题 历年西安电子科技大学计算机考研复试机试真题 更多学校题目开源地址&#xff1a;https://gitcode.com/verticallimit1/noobdream…

2025最新!9款AI论文软件测评:本科生写论文痛点全解析

2025最新&#xff01;9款AI论文软件测评&#xff1a;本科生写论文痛点全解析 2025年AI论文工具测评&#xff1a;为何值得一看&#xff1f; 随着人工智能技术的不断进步&#xff0c;越来越多的本科生开始依赖AI论文软件来提升写作效率。然而&#xff0c;面对市场上琳琅满目的工具…

ubuntu虚拟机mysql数据库忘记密码

ubuntu虚拟机mysql数据库忘记密码 ​ 上课时候&#xff0c;由于ubuntu虚拟机中的mysql数据库安装时是随机密码&#xff0c;导致pycharm连接时密码错误&#xff0c;以下是作者的解决办法。 文章目录ubuntu虚拟机mysql数据库忘记密码mysql安装以及spark中mysql依赖安装步骤 1&am…

学长亲荐8个AI论文工具,研究生轻松搞定开题报告!

学长亲荐8个AI论文工具&#xff0c;研究生轻松搞定开题报告&#xff01; AI工具让论文写作不再“卡壳” 在研究生阶段&#xff0c;论文写作是每位学生必须面对的挑战。无论是开题报告、文献综述还是最终的毕业论文&#xff0c;都需要大量的时间与精力。而随着AI技术的发展&…

Selenium + 超级鹰实现猎聘网滑块验证码自动登录

Selenium + 超级鹰实现猎聘网滑块验证码自动登录本文介绍如何使用 Selenium 驱动 Chrome 浏览器,结合超级鹰打码平台识别腾讯滑块验证码缺口位置,实现猎聘网(liepin.com)的自动登录。注意:本文仅用于技术学习与交…

2025年北京邮电大学计算机考研复试机试真题(附 AC 代码 + 解题思路)

2025年北京邮电大学计算机考研复试机试真题 2025年北京邮电大学计算机考研复试上机真题 历年北京邮电大学计算机考研复试上机真题 历年北京邮电大学计算机考研复试机试真题 更多学校题目开源地址&#xff1a;https://gitcode.com/verticallimit1/noobdream N 诺 DreamJudg…

「AI元人文构想」对话全记录:从困境、构想到系统自洽的七十日

「AI元人文构想」对话全记录&#xff1a;从困境、构想到系统自洽的七十日一、 缘起&#xff1a;穿透表象的野心与链接的失效 初始接触&#xff1a;用户首先分享了一篇题为《穿透表象&#xff1a;在“人类在环规则在场语境主权”框架下重审AI元人文构想的风险与未来》的论文摘要…

链表|160.相交链表234.回文指针141环形链表

相交链表点击查看代码 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public…

Linux中级の自动运维工具Ansible基础

Ansible基本概述 什么是Ansible? Ansible是一个自动化统一配置管理工具,自动化主要体现在 Ansible集成了丰富模块以及组件,可以通过一个命令完成一系列的 操作,进而能减少重复性的工作和维护成本,可以提高工作效率…

【图数据库与知识图谱入门】3.5 知识图谱的典型应用场景

文章目录 3.5 知识图谱的典型应用场景 3.5.1 智能搜索:知识增强型语义检索 应用概述 实战代码:基于Neo4j的影视知识智能搜索 环境准备 步骤1:构建影视知识图谱 步骤2:实现智能搜索功能 运行结果 3.5.2 个性化推荐:实体关联驱动的精准推荐 应用概述 实战代码:基于知识图谱…

04. 绘图功能

一、绘制直线我们可以在终端中使用 pip 安装 OpenCV 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载。 pip install opencv-pyt…

AcWing 338:计数问题 ← 数位DP

​【题目来源】https://www.acwing.com/problem/content/340/https://www.luogu.com.cn/problem/P2602【题目描述】给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0~9 的出现次数。例如,a=1024,b=1032,则 a 和 …

Java-Spring 依赖注入详解 - 从零开始理解 - 若

Spring 依赖注入详解 - 从零开始理解 📚 什么是依赖注入? 生活中的例子 想象一下,你是一个餐厅老板,你需要一个厨师来做饭。 传统方式(不用依赖注入): // 你需要自己去找厨师,自己雇用一个 public class Rest…

在 Cloud SQL for PostgreSQL 上启用 pgvector

本文档记录了如何在 Google Cloud SQL (PostgreSQL 13) 实例上启用 pgvector 扩展&#xff0c;以支持向量数据库功能&#xff08;如向量存储和相似度搜索&#xff09;。 1. 简介 pgvector 是 PostgreSQL 的一个开源扩展&#xff0c;用于存储和查询向量嵌入&#xff08;Vector E…

Doris为2.1版本,但json_each不可以用解决方法

Doris 2.1 版本&#xff08;原生支持json_each函数&#xff09;&#xff0c;但该函数无法使用&#xff0c;核心原因是FE 节点的功能开关未开启&#xff08;Doris 2.x 部分 JSON 高级函数默认关闭&#xff0c;需手动配置启用&#xff09;&#xff0c;而非版本不兼容。一、先排查…

《创业之路》-754-《架构思维:从程序员到CTO》第二部分:架构师的六大生存法则与启发

一、六大生存法则的核心内容《架构思维&#xff1a;从程序员到CTO》第二部分聚焦架构师在复杂环境中的生存法则&#xff0c;提出六大核心原则&#xff0c;这些法则基于经济学、社会学等规律&#xff0c;为架构活动提供底层逻辑支撑&#xff1a;有唯一且正确的目标核心&#xff…

Nature Genetics | 本周最新文献速递

文章标题:Multitrait analyses identify genetic variants associated with aortic valve function and aortic stenosis risk 中文标题:深度学习结合多性状分析,揭示主动脉瓣狭窄的遗传风险与干预靶点! 关键词: …

Java 反射机制解析:从基础概念到框架实践 - 教程

Java 反射机制解析:从基础概念到框架实践 - 教程2025-12-25 23:06 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displa…