当前位置: 首页 > 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.rkmt.cn/news/154322.html

相关文章:

  • 2025年西安电子科技大学计算机考研复试机试真题(附 AC 代码 + 解题思路)
  • Selenium + 超级鹰实现猎聘网滑块验证码自动登录
  • 2025年北京邮电大学计算机考研复试机试真题(附 AC 代码 + 解题思路)
  • 04. 绘图功能
  • AcWing 338:计数问题 ← 数位DP
  • 在 Cloud SQL for PostgreSQL 上启用 pgvector
  • Java-Spring 依赖注入详解 - 从零开始理解 - 若
  • 《创业之路》-754-《架构思维:从程序员到CTO》第二部分:架构师的六大生存法则与启发
  • Doris为2.1版本,但json_each不可以用解决方法
  • 微信小程序uniapp-vue校园租房指南房屋租赁
  • 数据增强(Data Augmentation)策略大全
  • Windows系统文件vsstrace.dll丢失损坏问题 下载修复方法
  • 微信小程序uniapp-vue校园美食评论餐饮配送商家 配送员
  • ATO、MTS、MTO、ETO、CTO:一文看懂制造业五大生产模式到底差在哪
  • Linux中将文本的奇数行和偶数行分别转换为单独的行
  • Windows系统文件wavemsp.dll丢失损坏 下载修复方法
  • 金字塔 物理动画
  • Windows系统文件vcomp100.dll丢失 下载修复
  • 基于SpringBoot的线上兼职招聘信息管理系统毕业设计项目源码
  • 2025最新!9个AI论文平台测评:研究生写论文痛点全解析
  • 测评5大DeepSeek推广公司,助力企业选对GEO服务商(2026年1月更新) - 品牌2025
  • Gin框架基础篇005_静态文件服务
  • 阅读笔记11
  • JavaScript 变量:let 和 const 该用谁?
  • 芒格的“多元思维模型“:提高投资决策的全面性
  • 做DeepSeek推广的公司,哪家比较靠谱?(2026年1月更新) - 品牌2025
  • 《数据采集与融合技术实践》综合设计——多源异构数据采集与融合应用综合实践
  • 北京种植义齿价格是多少
  • Python机器学习入门(Scikit-learn)教程:从环境搭建到实战建模
  • 2026年哪家AI公司的DeepSeek推广做的好? - 品牌2025