尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

Java-Spring 依赖注入详解--多个类实现与选择 - 若
📅 发布时间:2026/6/20 2:36:42

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

🤔 问题场景

假设你有一个 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
  • 全都要用 → 用 List 或 Map
  • 按配置选 → 用条件注解

🔍 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 实现类(如 RegisterReqHandler、InviteReqHandler 等)通过手动注册到 Map 中管理:

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

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

相关新闻

  • 2025年西安电子科技大学计算机考研复试机试真题(附 AC 代码 + 解题思路)
  • Selenium + 超级鹰实现猎聘网滑块验证码自动登录
  • 2025年北京邮电大学计算机考研复试机试真题(附 AC 代码 + 解题思路)

最新新闻

  • OpenClaw+飞书AI工作流:声明式Skill编排与企业级落地实践
  • 深入解析LPC2387:ARM7架构、双AHB总线与外设协同设计实战
  • 汽车照明驱动芯片MC17XSF500:通信保护与故障诊断机制深度解析
  • 2026蚌埠2026正规漏水检测维修公司精选口碑榜TOP5权威推荐-精准定位检测漏水点-专业防水补漏堵漏维修、卫生间/厨房/屋顶/天沟/地下室/阳台防水漏水检测维修 - 安佳防水
  • 3步实现股票智能分析自动化:零成本定时生成专业投资报告
  • 终极指南:5分钟在Mac上制作Windows启动盘,轻松绕过TPM限制

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号