[SpringBoot]接口的多实现:选择性注入SpringBoot接口的实现类
最近在项目中遇到两种情况,准备写个博客记录一下。
情况说明:Service层一个接口是否可以存在多个具体实现,此时应该如何调用Service(的具体实现)?
其实之前的项目中也遇到过这种情况,只不过我采用的方式是新建了一个Service接口,然后拆分两个实现类,让两个实现类分别实现两个不同的接口,然后在Controller中分别注入两个Service。最近项目中又遇到了这种情况,于是简单研究了一下,一个Service确实是可以有多个实现的,以下介绍4种区分实现类的方式。
以下代码本人均已测试,并对一些情况做了补充(欢迎继续补充)。
文章目录
- 实现
- 方式1: 直接使用实现类类名来区分
- 方式2:`@Qualifier`注解
- 方式3:设置主实现类
- 方式4:使用`@Resource`注解
- 总结
- 致谢
实现
方式1: 直接使用实现类类名来区分
我在以下代码中直接创建一个接口AaaService,然后让BbbServiceImpl和CccServiceImpl分别来实现AaaService,且BbbServiceImpl和CccServiceImpl注册为Service。
public interface AaaService {String say();
}
@Service
public class BbbServiceImpl implements AaaService {@Overridepublic String say() {System.out.println("我是BBB");return "我是BBB";}
}
@Service
public class CccServiceImpl implements AaaService {@Overridepublic String say() {System.out.println("我是CCC");return "我是CCC";}
}
以上代码AAA就有了两个Service实现类,尽管我没有遵循一些明明规则,但是BBB与CCC确实是AAA的实现类,且它们三个都是Service层的组件,接下来我在Controller中注入这两个组件。
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate AaaService bbbServiceImpl;@Autowiredprivate AaaService cccServiceImpl;@GetMapping("/b")public String BBB(){return bbbServiceImpl.say();}@GetMapping("/c")public String CCC(){return cccServiceImpl.say();}}
这时候我们启动项目,通过浏览器分别访问/test/b
与/test/c
地址,情况如下:
/test/b
:我是BBB(且后端控制台也输出:我是BBB)/test/c
:我是CCC(且后端控制台也输出:我是CCC)
因此,我们确实在Controller层同时注入了一个Service的两个实现类。
方式2:@Qualifier
注解
通常情况下,如果注入的B与C的变量名没有遵循与实现类同名(小驼峰)的命名方式的话,项目会启动失败,并且报错如下:
Action:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
表现如下:
这是在提示我们使用@Qualifier
注解来区分两个实现类(使用方式一也是可以的)。在一些情况下,我们虽然一个Service有多个实现类,但是我们依旧想用最传统的方式,也就是接口名小驼峰的方式来作为它的实现类来使用,但是很明显,像是下面这段代码在该情况下是不能生效的,因为我们并没有一个叫AaaService
的实现类。
@RestController
public class TestController {@Autowiredprivate AaaService aaaService;
}
上面这段代码运行起来之后就会报我们上面说到的报错,该如何解决呢??只需要在@Autowired
上面加上一个注解就可以,写法如下:
@RestController
public class TestController {@Qualifier("bbbServiceImpl")@Autowiredprivate AaaService aaaService;
}
注意,注解中写的内容必须是该Service接口的某个实现类的小驼峰名称。
这样一来,我们就在该Controller中注入了BbbServiceImpl
,只不过它的名称是aaaService
,在后续使用中,我们调用的也是BbbServiceImpl
种的方法。
方式3:设置主实现类
通过以上两种方式,我们已经可以实现自由的选择我们具体要注入的是哪一个实现类,但是如果业务中有这样的一个场景:我默认下只使用A实现类,BC实现类是我在特殊情况下才去使用的。在这种情况下,我希望能有一种“默认”机制的出现,该如何实现呢?请看以下代码:
public interface AaaService {// ...
}@Service
@Primary // 注意这一个注解
public interface BbbServiceImpl implements AaaService {// Aaa的实现 ...
}@Service
public interface CccServiceImpl implements AaaService {// Aaa的实现 ...
}
在以上代码中,由于B
实现上面有一个@Primary
注解,因此该实现类会被当做是A
接口的主要实现类,这时候我若是未指明具体使用的是哪一个实现类(就不会报错啦),就会默认用的是B实现类。
这时候又出现了一个小问题,如果这时候我想用C,该怎么办呢?换种方式问一下:命名这个东西是随便写的,如果我们恰巧如方式1中所说的,我变量名命名成了cccServiceImpl
,它注入的是B还是C呢?答案揭晓:是B。在此种情况下,用命名来区分实现类的方式已经失效了,要是想使用其他的实现类,只能使用方式二中的@Qualifier
注解来区分。
方式4:使用@Resource
注解
正如方式3中所说,我们设置了@Primary
之后,已经无法通过@Autowired
注解去控制注入哪个实现类了,但是有没有一种方法还是可以用名称去选择呢?当然可以,那就是@Resource
注解。
因为Resource注解默认使用名称进行依赖注入,所以情况3中的代码不变,我们对Controller做如下修改:
@RestController
public class TestController {@ResourceAaaService cccServiceImpl;@GetMapping("/test")public String test() {return cccServiceImpl.say();}}
在以上代码中,虽然我们已经对AaaService
设置了默认实现类BbbServiceImpl
,但是使用@Resource
注解注入了名称为cccServiceImpl
的实现,这时候访问/test
,得到的字符串依旧是"我是CCC"。
总结
- 结论:一个Service可以有多个实现,且我们可以手动选择具体使用的哪个实现
- 使用
@Autowired
且没有做过多操作(如设置默认实现)的情况下,我们可以使用注入的变量名称(一定是小驼峰)的方式选择变量名,也可以在@Autowired
上面添加@Qualifier("实现类的小驼峰")
的方式来选择具体实现类。 - 我们可以在接口实现类上写上
@Primary
注解来认证该实现类为主实现类,在该情况下,如果不特意指定是哪个实现类,则一律认为是默认实现类。 - 当指定了默认实现类之后,使用
@Autowired+小驼峰名称
的注入方式,已经无法指定特定实现类,只能使用``@Qualifier(“实现类的小驼峰”)@+@Autowired或
@Resource + 小驼峰命名`的方式进行区分。
致谢
感谢 [CSDN | springboot中一个service接口多个实现类,如何注入]
感谢 [稀土掘金 | spring接口多实现类,选择性注入的4种解决方案]
相关文章:

[SpringBoot]接口的多实现:选择性注入SpringBoot接口的实现类
最近在项目中遇到两种情况,准备写个博客记录一下。 情况说明:Service层一个接口是否可以存在多个具体实现,此时应该如何调用Service(的具体实现)? 其实之前的项目中也遇到过这种情况,只不过我采…...

北京大学 wlw机器学习2022春季期末试题分析
北京大学 wlw机器学习2022春季期末试题分析 前言新的开始第一题第二题第三题 前言 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的开始 第…...
前端文件下载方法(包含get和post)
export const downloadFileWithIframe (url, name) > {const iframe document.createElement(iframe);iframe.style.display none; // 防止影响页面iframe.style.height 0; // 防止影响页面iframe.name name;iframe.src url;document.body.appendChild(iframe); // 这…...

高性能、可扩展、支持二次开发的企业电子招标采购系统源码
在数字化时代,企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台,涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…...
2645. 构造有效字符串的最少插入数
Problem: 2645. 构造有效字符串的最少插入数 文章目录 解题思路解决方法复杂度分析代码实现 解题思路 解决此问题需要确定如何以最小的插入次数构造一个有效的字符串。首先,我们需要确定开头的差距,然后决定中间的补足,最后决定末尾的差距。…...

C#,快速排序算法(Quick Sort)的非递归实现与数据可视化
排序算法是编程的基础。 常见的四种排序算法是:简单选择排序、冒泡排序、插入排序和快速排序。其中的快速排序的优势明显,一般使用递归方式实现,但遇到数据量大的情况则无法适用。实际工程中一般使用“非递归”方式实现。 快速排序(Quick Sor…...

【操作系统xv6】学习记录2 -RISC-V Architecture
说明:看完这节,不会让你称为汇编程序员,知识操作系统的前置。 ref:https://binhack.readthedocs.io/zh/latest/assembly/mips.html https://www.bilibili.com/video/BV1w94y1a7i8/?p7 MIPS MIPS的意思是 “无内部互锁流水级的微…...

C++力扣题目111--二叉树的最小深度
力扣题目链接(opens new window) 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。 示例: 给定二叉树 [3,9,20,null,null,15,7], 返回它的最小深度 2 思路 看完了这篇104.二…...
【图像拼接】源码精读:Adaptive As-Natural-As-Possible Image Stitching(AANAP/ANAP)
第一次来请先看这篇文章:【图像拼接(Image Stitching)】关于【图像拼接论文源码精读】专栏的相关说明,包含专栏内文章结构说明、源码阅读顺序、培养代码能力、如何创新等(不定期更新) 【图像拼接论文源码精读】专栏文章目录 【源码精读】As-Projective-As-Possible Imag…...

解决docker run报错:Error response from daemon: No command specified.
将docker镜像export/import之后,对新的镜像执行docker run时报错: docker: Error response from daemon: No command specified. 解决方法: 方案1: 查看容器的command: docker ps --no-trunc 在docker run命令上增加…...

算法第十二天-最大整除子集
最大整除子集 题目要求 解题思路 来自[宫水三叶] 根据题意:对于符合要求的[整除子集]中的任意两个值,必然满足[较大数]是[较小数]的倍数 数据范围是 1 0 3 10^3 103,我们不可能采取获取所有子集,再检查子集是否合法的暴力搜解法…...
简单易懂的PyTorch 损失函数:优化机器学习模型的关键
目录 torch.nn子模块Loss Functions详解 nn.L1Loss 用途 用法 使用技巧 注意事项 代码示例 nn.MSELoss 用途 用法 使用技巧 注意事项 代码示例 nn.CrossEntropyLoss 用途 用法 使用技巧 注意事项 代码示例 使用类别索引 使用类别概率 nn.CTCLoss 用途 …...

Kubernetes/k8s的存储卷/数据卷
k8s的存储卷/数据卷 容器内的目录和宿主机的目录挂载 容器在系统上的生命周期是短暂的,delete,k8s用控制创建的pod,delete相当于重启,容器的状态也会回复到初始状态 一旦回到初始状态,所有的后天编辑的文件都会消失…...

【漏洞复现】锐捷RG-UAC统一上网行为管理系统信息泄露漏洞
Nx01 产品简介 锐捷网络成立于2000年1月,原名实达网络,2003年更名,自成立以来,一直扎根行业,深入场景进行解决方案设计和创新,并利用云计算、SDN、移动互联、大数据、物联网、AI等新技术为各行业用户提供场…...
Android - 串口通讯(SerialPort)
最早的博客Android 模拟串口通信过程_launch virtual serial port driver pro-CSDN博客里就是用过 Google 提供的 demo,最近想再写个其他的demo发现用起来有点麻烦,还需要导入其他 module,因此在网上找到了Android-SerialPort-API: https://g…...
如何使用設置靜態住宅IP
靜態住宅IP就是一種靜態的、分配給住宅用戶的IP地址。與動態IP地址不同,靜態住宅IP一旦分配給用戶,就會一直保持不變,除非ISP(Internet Service Provider,互聯網服務提供商)進行手動更改。那麼,…...

在学习爬虫前的准备
1. 写一个爬虫程序需要分几步 获取网页内容。 我们会通过代码给一个网站服务器发送请求,它会返回给我们网页上的内容。 在我们平时使用浏览器访问服务器内容是,本质上也是向服务器发送一个请求,然后服务器返回网页上的内容。只不过浏览器还会…...

windows下安装oracle-win-64-11g超详细图文步骤
官方下载地址:点这里 1.根据自己电脑情况,解压64或者32位客户端,以及database压缩包 2.解压后双击执行database文件夹下的setup.exe 3.详细的安装步骤 (1)数据库安装 一、配置安全更新 电子邮件可写可不写…...

Go模板后端渲染时vue单页面冲突处理
go后端模版语法是通过 {{}} ,vue也是通过双花括号来渲染的,如果使用go渲染vue的html页面的时候就会报错,因为分别不出来哪个是vue的,哪个是go的,既可以修改go的模板语法 template.New("output").Delims(&qu…...

笔记本摄像头模拟监控推送RTSP流
使用笔记本摄像头模拟监控推送RTSP流 一、基础安装软件准备 本文使用软件下载链接:下载地址 FFmpeg软件: Download ffmpeg 选择Windows builds by BtbN 一个完整的跨平台解决方案,用于录制、转换和流式传输音频和视频。 EasyDarwin软件:Download Easy…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...