[SpringBoot]接口的多实现:选择性注入SpringBoot接口的实现类

news2024/11/13 10:26:03

最近在项目中遇到两种情况,准备写个博客记录一下。

情况说明: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 {
    @Override
    public String say() {
        System.out.println("我是BBB");
        return "我是BBB";
    }
}
@Service
public class CccServiceImpl implements AaaService {
    @Override
    public String say() {
        System.out.println("我是CCC");
        return "我是CCC";
    }
}

以上代码AAA就有了两个Service实现类,尽管我没有遵循一些明明规则,但是BBB与CCC确实是AAA的实现类,且它们三个都是Service层的组件,接下来我在Controller中注入这两个组件。

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private AaaService bbbServiceImpl;

    @Autowired
    private 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 {
  @Autowired
  private AaaService aaaService;
}

上面这段代码运行起来之后就会报我们上面说到的报错,该如何解决呢??只需要在@Autowired上面加上一个注解就可以,写法如下:

@RestController
public class TestController {
  @Qualifier("bbbServiceImpl")
  @Autowired
  private 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 {
  
  @Resource
  AaaService cccServiceImpl;
  
  @GetMapping("/test")
  public String test() {
    return cccServiceImpl.say();
  }
  
}

在以上代码中,虽然我们已经对AaaService设置了默认实现类BbbServiceImpl,但是使用@Resource注解注入了名称为cccServiceImpl的实现,这时候访问/test,得到的字符串依旧是"我是CCC"。





总结

  1. 结论:一个Service可以有多个实现,且我们可以手动选择具体使用的哪个实现
  2. 使用@Autowired且没有做过多操作(如设置默认实现)的情况下,我们可以使用注入的变量名称(一定是小驼峰)的方式选择变量名,也可以在@Autowired上面添加@Qualifier("实现类的小驼峰")的方式来选择具体实现类。
  3. 我们可以在接口实现类上写上@Primary注解来认证该实现类为主实现类,在该情况下,如果不特意指定是哪个实现类,则一律认为是默认实现类。
  4. 当指定了默认实现类之后,使用@Autowired+小驼峰名称的注入方式,已经无法指定特定实现类,只能使用``@Qualifier(“实现类的小驼峰”)@+@Autowired@Resource + 小驼峰命名`的方式进行区分。





致谢

感谢 [CSDN | springboot中一个service接口多个实现类,如何注入]

感谢 [稀土掘金 | spring接口多实现类,选择性注入的4种解决方案]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1366272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTF之逆向入门

逆向工程(Reverse Engineering),又称反向工程,是一种技术过程,即对一项目标产品进行逆向分析及研究,从而演绎并得出该产品的处理流程、组织结构、功能性能规格等设计要素,以制作出功能相近,但又不完全一样的…

微信私密朋友圈被吐槽有BUG

日前,大量网友在各社交媒体上讨论微信私密朋友圈出现 Bug 的话题,起因是跨年期间一个网友发布了一条”私密朋友圈,但不一会就收到朋友发来的信息,”又偷偷发朋友圈了?“,估计此时网友可能已经”寒毛四起、汗…

物理机搭建hive

一、修改Hadoop配置 修改core-site.xml 配置yarn-site.xml 分发文件,然后重启集群 二、 Hive解压安装 上传文件 添加hive环境便量,source生效 启动高可用集群,启动hive 三、配置mysql元数据库 检查当前系统是否安装过Mysql&#xf…

基于MATLAB车牌识别系统设计

基于MATLAB车牌识别系统设计 一、设计方案 智能交通系统已成为现代社会道路交通发展趋势。在智能交通系统中,车牌自动识别系统是一个非常重要的发展方向。对于车牌识别系统的要满足当车辆通过摄像头采集车辆图片,将其图片进行图像预处理、车牌定位、字符…

关于FPGA仿真复位信号和实际板子复位信号的吐血大坑

最近在研究mipi dphy IP下载到板子上进行验证的初步流程问题 IP的example案例里面,系统复位全是高电平复位,低电平工作 想当然的打通IP流程,写工程的时候也使用了高电平复位,低电平复位 结果调试了N轮,init_done信号一…

通用web自动扩缩容_智能运维引擎CudgX

一、概述 CudgX是星汉未来自主研发的面向云原生时代的智能运维引擎,支持根据 MetricQPS 分段耗时指标进行自动扩缩容。 通过各类服务的多维度、大规模的日志数据采集以及机器学习训练分析,对服务进行数字化、指标化度量,并基于部署的…

基础算法【解题思路】:单链表的倒数第k个节点

定义指针p1,让p1走k步: 定义指针p2,在p1走了k步的时候,p2也跟着走。 p1走到最后的时候走了n-k步,停留在最后的null结点。 P2从头结点开始,也跟着走到了n-k步,而n-k恰好是倒数第k个节点。 例…

TypeScript 从入门到进阶之基础篇(六) 类型(断言 、推论、别名)| 联合类型 | 交叉类型

系列文章目录 TypeScript 从入门到进阶系列 TypeScript 从入门到进阶之基础篇(一) ts基础类型篇TypeScript 从入门到进阶之基础篇(二) ts进阶类型篇TypeScript 从入门到进阶之基础篇(三) 元组类型篇TypeScript 从入门到进阶之基础篇(四) symbol类型篇TypeScript 从入门到进阶…

css - 渐变样式实现(渐变边框 + 渐变背景)

.select {border-bottom: 2px solid; /* 下边框 */border-image: linear-gradient(90deg,rgba(250, 173, 20, 0) 0%,rgba(250, 173, 20, 1) 51.46%,rgba(125, 87, 10, 0) 100%)1; /* 边框渐变 */background-image: radial-gradient(circle at 49% 238%, #faad14, transparent 7…

2023APMCM亚太数学建模C题 - 中国新能源汽车的发展趋势(2)

五.问题二模型建立和求解 5.1 问题二模型建立和求解 针对题目二,题目要求收集中国新能源电动汽车行业发展数据,建立数学模型描述,并预测未来十年的发展。由于在第一文中,我们已经收集了一定的新能源行业发展数据&…

马尔可夫算法及其实例(预测类模型)

马尔科夫预测模型是一种基于马尔科夫过程的预测方法。马尔科夫过程是一类具有马尔科夫性质的随机过程,即未来的状态只依赖于当前状态,而与过去状态无关。这种过程通常用状态空间和状态转移概率矩阵来描述。 在马尔科夫预测模型中,系统被建模为…

提升工作效率:IDEA配置优化总结指南

idea 配置优化总结 配置优化优化性能优化JVM参数使用 jconsole 监控 full gc 频率关闭代码检查设置编译进程和Maven的堆值取消自动构建 修改快捷键System Settings个性化设置设置主题修改字体Color SchemeCode Style 包类设置设置maven自动导包显示pom依赖关系图,解…

【docker笔记】Docker网络

Docker网络 容器间的互联和通信以及端口映射 容器IP变动时候可以通过服务名直接网络通信而不受到影响 常用命令 查看网络 docker network ls创建网络 docker network create XXX网络名字查看网络源数据 docker network inspect XXX网络名字删除网络 docker network rm…

可视化监控EasyCVR视频分析/云存储平台iframe地址播放异常该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

Pytorch框架学习笔记

官网- PyTorch Tensor 构造随机初始化矩阵 xtorch.rand(5,3) 构造全0矩阵,数据类型为long xtorch.zeros(5,3,dtypetorch.long) 获取维度信息 x.size() tensor加法 torch.add(x,y) xy y…

如何用Chat分析本地化运维服务有哪些?

问CHAT:本地化运维服务有哪些? CHAT回复:本地化运维服务主要包括以下几大部分: 1. 系统监控和管理:密切关注系统的性能,实时发现并解决可能出现的问题,以确保服务的稳定和可用性。 2. 数据备份…

python 各级目录文件读取

目录结构 import pytestdef test_01():# 同级文件with open(1.txt, r, encodingutf-8) as file:content file.read()print(content)def test_02():# 同级目录的下的文件with open(rupfile/2.txt, r, encodingutf-8) as file:content file.read()print(content)def test_03():…

Python编程+copilot+代码补全+提高效率

Python编程copilot代码补全提高效率 copilot是由Github和OpenAI合作开发的一款AI编程工具,它可以根据自然语言或部分代码,自动给出合适的代码补全建议。copilot支持多种编程语言,包括Python,也可以在Pycharm等主流IDE中使用。本资…

Java学习苦旅(二十六)——反射,枚举和lamda表达式

本篇博客将讲解反射,枚举和lamda表达式。 文章目录 反射定义用途反射基本信息反射相关的类Class类Class类中相关的方法 反射示例反射的优缺点优点缺点 枚举背景及定义常用方法枚举优缺点优点缺点 Lambda表达式背景语法函数式接口定义基本使用 变量捕获Lambda在集合…

基于docker环境搭建Mysql主从

文章目录 Mysql主从搭建1.1 Master搭建1.2 Slave搭建1.3 主从复制 1.4 验证 Mysql主从搭建 ​ mysql主从复制的原理将主数据库的增删改查等操作记录到二进制日志文件中,从库接收主库日志文件,根据最后一次更新的 起始位置,同步复制到从数据…