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

Spring Boot项目里多个Service冲突了?用@Primary注解5分钟搞定默认Bean选择

Spring Boot多Service冲突实战Primary注解的精准应用指南在Spring Boot开发中我们经常会遇到一个典型问题当项目中有多个同类型Service实现时自动装配(Autowiring)会突然报错或者选择了不符合预期的实现类。这种情况在微服务架构中尤为常见比如用户注册时需要同时支持邮件和短信通知支付模块需要对接多个第三方渠道。本文将从一个真实的用户注册场景出发带你彻底理解如何用Primary注解优雅解决这类问题。1. 问题复现当自动装配遇到多实现类假设我们正在开发一个电商平台的用户注册模块需要同时支持邮件和短信两种通知方式。按照常规做法我们可能会这样实现public interface NotificationService { void sendNotification(String message, String recipient); } Service public class EmailNotificationService implements NotificationService { Override public void sendNotification(String message, String recipient) { System.out.println(发送邮件到 recipient message); // 实际邮件发送逻辑... } } Service public class SmsNotificationService implements NotificationService { Override public void sendNotification(String message, String recipient) { System.out.println(发送短信到 recipient message); // 实际短信发送逻辑... } }然后在用户服务中注入通知服务Service public class UserService { Autowired private NotificationService notificationService; public void registerUser(User user) { // 用户注册逻辑... notificationService.sendNotification(欢迎注册, user.getPhone()); } }启动应用时Spring会抛出NoUniqueBeanDefinitionException异常提示我们有两个NotificationService的实现类它不知道应该注入哪一个。这就是典型的多实现类冲突问题。2. 解决方案Primary的核心机制Primary注解是Spring框架提供的一个简单而强大的工具用于解决这种多实现类冲突。它的作用原理非常直观当Spring容器中存在多个同类型bean时如果其中一个bean标记了Primary在没有其他限定条件的情况下Spring会优先选择这个bean进行注入让我们修改前面的例子指定邮件通知为默认实现Service Primary public class EmailNotificationService implements NotificationService { // 实现保持不变 }现在当UserService请求注入NotificationService时Spring会自动选择EmailNotificationService。这种方式有几个显著优势非侵入性不需要修改使用方的代码灵活性可以随时更换默认实现而不影响其他代码明确性在代码中清晰表达了设计意图3. 深入Primary配置类中的高级用法除了在实现类上直接标注Primary在Java配置类中同样适用这种方式更适合集中管理bean的优先级Configuration public class NotificationConfig { Bean Primary public NotificationService emailNotificationService() { return new EmailNotificationService(); } Bean public NotificationService smsNotificationService() { return new SmsNotificationService(); } }配置类方式特别适合以下场景当实现类来自第三方库无法直接修改源码时需要根据条件动态决定哪个实现作为主选希望将所有bean定义集中管理提高可维护性4. Primary与Qualifier的对比选择Spring提供了多种解决依赖冲突的方案最常用的除了Primary还有Qualifier。下表对比了两者的主要区别特性PrimaryQualifier使用场景指定默认实现精确指定特定实现配置位置实现类或Bean方法注入点(Autowired处)代码侵入性低(只需标注一次)高(每个注入点都需要指定)运行时灵活性较高(只需修改一处)较低(需要修改多处注入点)与Profile结合支持良好需要额外配置代码可读性实现类自身表明优先级使用方需要了解具体实现细节实际项目中两者经常配合使用Service public class UserService { Autowired Qualifier(smsNotificationService) private NotificationService notificationService; // 当需要短信通知时使用限定符 // 其他情况会回退到Primary的实现 }选择原则优先使用Primary当有一个明显的默认实现时必要时使用Qualifier当需要精确控制特定注入点时可以组合使用用Primary处理大多数情况Qualifier处理特殊情况5. 实战技巧与常见陷阱在实际项目中使用Primary时有几个关键点需要注意5.1 多Primary冲突如果意外地为多个同类型bean标记了PrimarySpring会抛出NoUniqueBeanDefinitionException。解决方法检查并确保只有一个Primary使用Qualifier明确指定通过条件注解(如Profile)控制不同环境下的主选beanService Primary Profile(prod) public class ProdNotificationService implements NotificationService { // 生产环境默认实现 } Service Primary Profile(dev) public class DevNotificationService implements NotificationService { // 开发环境默认实现 }5.2 继承体系中的优先级当处理类继承关系时Primary的行为可能会出人意料public interface BaseService {} public interface ExtendedService extends BaseService {} Service public class BaseServiceImpl implements BaseService {} Service Primary public class ExtendedServiceImpl implements ExtendedService {}在这种情况下BaseService的注入不会选择ExtendedServiceImpl因为它们不在同一个类型层次上。5.3 测试环境中的特殊处理单元测试中可能需要覆盖Primary的选择SpringBootTest public class UserServiceTest { MockBean Primary // 覆盖应用中的Primary private NotificationService mockNotificationService; Test public void testRegisterUser() { // 测试逻辑... } }6. 架构层面的应用思考Primary不仅仅是一个技术注解它在系统架构设计中也能发挥重要作用适配器模式实现为同一接口提供多种实现运行时选择默认适配器策略模式简化减少策略选择逻辑用Primary指定默认策略多环境配置如前所示结合Profile管理不同环境下的默认组件特性开关通过条件注解实现功能的动态启用/禁用Service Primary ConditionalOnProperty(name features.email-notification, havingValue true) public class EmailNotificationService implements NotificationService { // 实现 } Service Primary ConditionalOnProperty(name features.email-notification, havingValue false) public class SmsNotificationService implements NotificationService { // 实现 }这种模式让我们可以通过简单的配置切换来改变系统行为而不需要修改代码逻辑。

相关文章:

Spring Boot项目里多个Service冲突了?用@Primary注解5分钟搞定默认Bean选择

Spring Boot多Service冲突实战:Primary注解的精准应用指南 在Spring Boot开发中,我们经常会遇到一个典型问题:当项目中有多个同类型Service实现时,自动装配(Autowiring)会突然报错,或者选择了不符合预期的实现类。这种…...

用手机和一根绳子就能测重力加速度?手把手教你用Phyphox搞定大学物理实验

用手机和一根绳子就能测重力加速度?手把手教你用Phyphox搞定大学物理实验 记得大学物理实验课上,第一次看到单摆测重力加速度的实验装置时,我盯着那个精致的金属摆球和光学位移传感器看了半天。当时就在想:这么简单的物理原理&…...

告别单调报表:手把手教你用REUSE_ALV_GRID_DISPLAY_LVC打造高亮业务数据的SAP ALV

数据可视化革命:用SAP ALV动态样式提升业务洞察力 财务总监盯着屏幕上密密麻麻的数字报表,眉头紧锁——那些需要特别关注的异常数据,在千篇一律的黑白表格中几乎"隐身"。销售经理每周都要手动标注Top客户,重复劳动消耗…...

掌握bypy文件对比:3步实现百度云与本地文件完美同步

掌握bypy文件对比:3步实现百度云与本地文件完美同步 【免费下载链接】bypy Python client for Baidu Yun (Personal Cloud Storage) 百度云/百度网盘Python客户端 项目地址: https://gitcode.com/gh_mirrors/by/bypy 在Linux环境下管理百度云存储空间时&…...

G-Helper终极指南:3大技巧解锁华硕笔记本性能优化与风扇控制

G-Helper终极指南:3大技巧解锁华硕笔记本性能优化与风扇控制 【免费下载链接】g-helper G-Helper is a fast, native tool for tuning performance, fans, GPU, battery, and RGB on any Asus laptop or handheld - ROG Zephyrus, Flow, Strix, TUF, Vivobook, Zenb…...

SendPortal常见问题与故障排除:从安装到使用的完整解决方案

SendPortal常见问题与故障排除:从安装到使用的完整解决方案 【免费下载链接】sendportal Open-source self-hosted email marketing. Manage your own newsletters at a fraction of the cost. 项目地址: https://gitcode.com/gh_mirrors/se/sendportal Sen…...

AzurLaneAutoScript完全指南:7×24小时碧蓝航线自动化管家

AzurLaneAutoScript完全指南:724小时碧蓝航线自动化管家 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript AzurLan…...

告别红光干扰!OpenMV图像参数调优实战:解决电赛追踪中‘黑色胶带吸光’难题

OpenMV图像参数调优实战:攻克黑色背景下的红色激光追踪难题 在机器视觉应用场景中,黑色背景上的红色激光点追踪是一个经典但极具挑战性的问题。特别是在全国大学生电子设计竞赛这类对实时性和准确性要求极高的场合,如何确保OpenMV稳定识别红色…...

TTTAttributedLabel终极性能测试:大数据量文本渲染深度分析

TTTAttributedLabel终极性能测试:大数据量文本渲染深度分析 【免费下载链接】TTTAttributedLabel A drop-in replacement for UILabel that supports attributes, data detectors, links, and more 项目地址: https://gitcode.com/gh_mirrors/tt/TTTAttributedLab…...

Win10网络邻居一片空白?别急着重装,先试试这5个关键设置(保姆级排查指南)

Win10网络邻居一片空白?5个关键设置帮你精准定位问题 刚入职的新同事小李急匆匆跑过来:"王哥,我电脑怎么看不到部门共享盘了?行政刚发的通知都在里面!"看着他满头大汗的样子,我笑了笑&#xff1a…...

如何在3步内实现微信双设备登录:Xposed Hook技术深度解析

如何在3步内实现微信双设备登录:Xposed Hook技术深度解析 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad WeChatPad是一个基于Xposed框架的Android模块,通过Hook技术强制微信以平板模式…...

(第二十八篇)OpenClaw成本与感知的奇点——从“Token封建制”到“全民养虾”的本体论地基

OpenClaw成本与感知的奇点——从“Token封建制”到“全民养虾”的本体论地基(第二十八篇)核心更新覆盖:4月24日(DeepSeek V4集成、成本降低、开源模型内化)、4月25日(高危漏洞修复、配置管理强化、稳健性加…...

OpenClaw AI Agent 生产级可观测性实战:基于 OpenTelemetry 与 Logfire

1. 项目概述:为OpenClaw注入生产级可观测性如果你正在使用OpenClaw构建AI Agent,并且已经过了“本地跑通”的兴奋期,开始思考如何把它部署到真实的生产环境中,那么你大概率会遇到一个核心痛点:“黑盒”问题。Agent内部…...

用OpenMV+STM32做小车跟踪,PID参数到底怎么调?我的调试笔记分享

OpenMVSTM32智能车PID调参实战:从振荡到平稳的调试心法 第一次看到自己组装的小车像醉汉一样左右摇摆时,我盯着满地散落的二维码碎片苦笑——这已经是今天第三次撞上前车了。作为电赛常见的经典题型,基于OpenMV视觉的智能车跟踪看似简单&…...

AntiMicroX:免费开源的终极游戏手柄键盘映射工具,让所有游戏支持手柄操作

AntiMicroX:免费开源的终极游戏手柄键盘映射工具,让所有游戏支持手柄操作 【免费下载链接】antimicrox Graphical program used to map keyboard buttons and mouse controls to a gamepad. Useful for playing games with no gamepad support. 项目地…...

告别格式壁垒:手把手教你用CAD Exchanger SDK + Eyeshot为.NET应用集成多CAD格式可视化

工业级CAD可视化集成实战:基于CAD Exchanger SDK与Eyeshot的.NET解决方案 在工业软件领域,处理多源异构CAD数据一直是开发者的痛点。不同供应商使用CATIA、SolidWorks等不同工具生成的STEP、JT等格式文件,往往导致企业内部设计评审和客户门户…...

3步高效提取Godot游戏资源:实用解包指南与进阶技巧

3步高效提取Godot游戏资源:实用解包指南与进阶技巧 【免费下载链接】godot-unpacker godot .pck unpacker 项目地址: https://gitcode.com/gh_mirrors/go/godot-unpacker 你是否曾试图分析Godot引擎开发的游戏资源,却被.pck文件格式阻挡&#xff…...

3分钟快速上手:Nucleus Co-op让你在单台电脑上享受本地分屏游戏乐趣

3分钟快速上手:Nucleus Co-op让你在单台电脑上享受本地分屏游戏乐趣 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 还在为喜欢的PC游戏…...

从手机快充到服务器电源:拆解5个真实产品,看LLC电路如何‘统治’高效电源设计

从手机快充到服务器电源:拆解5个真实产品,看LLC电路如何‘统治’高效电源设计 在电子设备功率需求爆发式增长的今天,电源设计工程师们面临着一个看似矛盾的挑战:如何在更小的空间内实现更高的能量转换效率?当我们拆解从…...

Amlogic-S9xxx-Armbian实战指南:让电视盒子变身全功能Linux服务器

Amlogic-S9xxx-Armbian实战指南:让电视盒子变身全功能Linux服务器 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905…...

打卡信奥刷题(3199)用C++实现信奥题 P8106 [Cnoi2021] 数学练习

P8106 [Cnoi2021] 数学练习 题目背景 「Cnoi2021」Cirno’s Easy Round II 热身赛开始了。 题目描述 为了让选手们重视文化课,Cirno 特意加入了一道 Kamishirasawa Keine 老师的数学练习:求将一个集合 U{1,2,3,⋯ ,n}\texttt{U}\{1,2,3,\cdots,n\}U{1,2…...

sandman2与Flask集成教程:如何在现有Web应用中嵌入自动化API服务

sandman2与Flask集成教程:如何在现有Web应用中嵌入自动化API服务 【免费下载链接】sandman2 Automatically generate a RESTful API service for your legacy database. No code required! 项目地址: https://gitcode.com/gh_mirrors/sa/sandman2 sandman2是…...

CP Editor完全指南:5分钟快速上手,从零开始算法竞赛

CP Editor完全指南:5分钟快速上手,从零开始算法竞赛 【免费下载链接】cpeditor The IDE for competitive programming :tada: | Fetch, Code, Compile, Run, Check, Submit :rocket: 项目地址: https://gitcode.com/gh_mirrors/cp/cpeditor CP Ed…...

Fish Speech-1.5多语种TTS教程:如何为不同语种选择最优参考音频与prompt

Fish Speech-1.5多语种TTS教程:如何为不同语种选择最优参考音频与prompt 想用AI生成不同语言的语音,但效果总是不尽如人意?声音要么生硬,要么带着奇怪的“外国腔”?别担心,这很可能不是你技术不行&#xf…...

为内部知识库问答系统集成 Taotoken 以灵活调用不同厂商的嵌入模型

为内部知识库问答系统集成 Taotoken 以灵活调用不同厂商的嵌入模型 1. 知识库问答系统的嵌入模型需求 在企业内部知识库问答系统的构建过程中,嵌入模型的质量直接影响检索增强生成(RAG)的效果。传统方案通常绑定单一厂商的嵌入API&#xff…...

Universal Extractor 2:500+文件格式一键提取的终极解决方案

Universal Extractor 2:500文件格式一键提取的终极解决方案 【免费下载链接】UniExtract2 Universal Extractor 2 is a tool to extract files from any type of archive or installer. 项目地址: https://gitcode.com/gh_mirrors/un/UniExtract2 你是否曾经…...

Penlight完全指南:10个核心模块助你快速提升Lua开发效率

Penlight完全指南:10个核心模块助你快速提升Lua开发效率 【免费下载链接】Penlight A set of pure Lua libraries focusing on input data handling (such as reading configuration files), functional programming (such as map, reduce, placeholder expressions…...

3步快速掌握:WindowResizer终极窗口尺寸强制调整工具完整指南

3步快速掌握:WindowResizer终极窗口尺寸强制调整工具完整指南 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些顽固的应用程序窗口而烦恼吗?那些无…...

别再瞎调了!WPF Grid布局里Auto和*的5个实战用法(附避坑指南)

别再瞎调了!WPF Grid布局里Auto和*的5个实战用法(附避坑指南) Grid布局是WPF中最常用的布局容器之一,但很多开发者在使用Auto和这两种尺寸定义方式时常常陷入误区。本文将深入探讨五种实际开发场景中Auto和的正确用法,…...

小红书数据采集终极指南:5步解锁内容运营新高度

小红书数据采集终极指南:5步解锁内容运营新高度 【免费下载链接】xhs 基于小红书 Web 端进行的请求封装。https://reajason.github.io/xhs/ 项目地址: https://gitcode.com/gh_mirrors/xh/xhs 你是否在为小红书内容运营缺乏数据支持而苦恼?想通过…...