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

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

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

🤔 问题场景

假设你有一个 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.jsqmd.com/news/140184/

相关文章:

  • 一键激活 Windows 与 Office 的轻量绿色工具!
  • centos7配置yum软件源
  • 2025年西安电子科技大学计算机考研复试机试真题(附 AC 代码 + 解题思路)
  • 学长亲荐8个AI论文工具,研究生轻松搞定开题报告!
  • 2025最新!9款AI论文软件测评:本科生写论文痛点全解析
  • ubuntu虚拟机mysql数据库忘记密码
  • Selenium + 超级鹰实现猎聘网滑块验证码自动登录
  • 2025年北京邮电大学计算机考研复试机试真题(附 AC 代码 + 解题思路)
  • 「AI元人文构想」对话全记录:从困境、构想到系统自洽的七十日
  • 链表|160.相交链表234.回文指针141环形链表
  • Linux中级の自动运维工具Ansible基础
  • 【图数据库与知识图谱入门】3.5 知识图谱的典型应用场景
  • 04. 绘图功能
  • AcWing 338:计数问题 ← 数位DP
  • Java-Spring 依赖注入详解 - 从零开始理解 - 若
  • 在 Cloud SQL for PostgreSQL 上启用 pgvector
  • Doris为2.1版本,但json_each不可以用解决方法
  • 《创业之路》-754-《架构思维:从程序员到CTO》第二部分:架构师的六大生存法则与启发
  • Nature Genetics | 本周最新文献速递
  • Java 反射机制解析:从基础概念到框架实践 - 教程
  • 微信小程序uniapp-vue校园租房指南房屋租赁
  • 模型调优技巧:提升准确率的10种实用方法
  • 149_尚硅谷_数组应用实例(1)
  • PCIe-浅谈Transaction ID和Tag(2)
  • 数据增强(Data Augmentation)策略大全
  • 软件缺少vfp9r.dll文件 无法启动运行问题 下载修复方法
  • 微信小程序uniapp-vue校园网络维修报修平 多媒体设备报修
  • PCIe-Tag Rule(2)
  • 别只测功能:一套可落地的鸿蒙分布式压力测试方案
  • 微信小程序uniapp-vue校园美食评论餐饮配送商家 配送员