Java SPI 原理、样例

在 Java 中,SPI(Service Provider Interface)全称为服务提供者接口,它是一种用于实现框架扩展和插件化的机制。

一、SPI 作用

允许在运行时动态地为接口查找服务实现,而不需要在代码中显式地指定具体的实现类。

这使得框架具有更好的可扩展性,第三方可以通过实现特定的接口来为框架提供额外的功能。这在很多框架和库中都有广泛的应用,例如数据库驱动、日志框架等。

二、基本原理

1、定义服务接口:首先定义一个接口或抽象类,这是服务的规范。
2、实现服务接口:编写接口的具体实现类。
3、注册服务实现:在 META-INF/services 目录下创建一个以接口全限定名为文件名的文件,并在该文件中列出所有实现类的全限定名。
4、加载服务实现:使用 ServiceLoader 类来加载和使用服务实现。

三、代码样例

1、定义服务接口

假设现在有一个权威机构,比如 Java,它需要对数据存储进行规范。

它定义了一个数据存储接口,和一个加载实现类的工具类。

package com.storage.specification;/*** 数据存储接口*/
public interface StorageProvider {void save(String data);
}
---------------------------------------------------
package com.storage.specification.spi;import com.storage.specification.StorageProvider;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;/*** 加载 存储服务 工具*/
public class ProviderLoader {public static StorageProvider getStorageProvider(ClassLoader classLoader) {// 从类路径 resources/META-INF/services 下加载 存储服务提供者ServiceLoader<StorageProvider> loader = ServiceLoader.load(StorageProvider.class, classLoader);Iterator<StorageProvider> iterator = loader.iterator();List<StorageProvider> providers = new ArrayList<>();while (iterator.hasNext()) {providers.add(iterator.next());}return providers.get(0);}
}

 步骤:

1)用IDEA 新建一个 maven 项目,取名叫 storage-specification

72210ef906724b369f01d2e4e93435c1.png

 

2)新建上述两个类

ef918c0b36394b809b2bf1b165f76832.png

 

3)install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

3f98b8a6f289464a86ba527dbecfacf3.png

 

2、实现服务接口

 现在有两个供应商 Mysql 和 Redis,它们拿到 Java 给的规范,分别提供他们的 二进制文件 存储实现和内存数据库实现。

Mysql 实现:

1)用IDEA 新建一个 maven 项目,取名叫 mysql-storage-provider

ce6ec51b40b84682bb9cf38d585bbeba.png

 

2)pom 文件依赖 存储规范 gav

d659cb7e4fe64bfa974a7c4a1aeb8ef5.png

 

3) 编写实现接口

package com.mysql.storage;import com.storage.specification.StorageProvider;public class MysqlStorageProvider implements StorageProvider {@Overridepublic void save(String s) {System.out.println("mysql save " + s);}
}

4)在项目 resources/META-INF/services 目录下

创建一个以接口全限定名 

com.storage.specification.StorageProvider 为文件名的文件(注意不要带后缀),

并在该文件中列出所有实现类的全限定名 com.mysql.storage.MysqlStorageProvider

83c56e97667248b4802bbde0f36f0013.png

 

5)install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

422d9523acff44959d660b62988bc35f.png

 以同样的方式创建一个 Redis 项目,取名 redis-storage-provider

576d491552644239b74f4243976001cb.png

 

区别只在

1)实现类不同

package com.redis.storage;import com.storage.specification.StorageProvider;public class RedisStorageProvider implements StorageProvider {@Overridepublic void save(String s) {System.out.println("redis save " + s);}
}

2) 实现类的全限定名不同

f9505b6029a4405ebff457d9ce1d567d.png

 

3)同样 install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

8a66d82e0ea14976b27b0ea2c2cfbdfa.png

3、定义服务使用者

1)用 IDEA 新建一个 maven 项目,取名叫 storage-user

b92732ff51ce4073879f42fdd7b8bada.png

2)pom 依赖引入 规范 gav 和 mysql gav

<dependencies><dependency><groupId>com.storage.specification</groupId><artifactId>storage-specification</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.mysql.storage</groupId><artifactId>mysql-storage-provider</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

 3) 调用 mysql 存储

0e235b38189546b28c369ee0e7627049.png

4)切换 redis 依赖

<dependencies><dependency><groupId>com.storage.specification</groupId><artifactId>storage-specification</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.redis.storage</groupId><artifactId>redis-storage-provider</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

 5)调用 redis 存储

可以看到,我们只修改了依赖的实现包,并没有修改代码,就实现了服务的替换。

而且我们的代码中并没有显示地调用服务的实现类。

355d9b822bfa4533b64df3954ee6ef98.png

四、总结

1、SPI 是 JAVA 提供给我们的一种用于实现框架扩展和插件化的机制。它可以让我们在不修改代码的时候,很轻松地替换服务的实现。

2、Spring 有自己的 SPI,Spring 的 SPI 路径是 resources/META-INF/spring.factories。

3、SPI 机制在框架集成中很常见。像 javax.validation 就是通过 SPI 的方式集成了 HibernateValidator 实现。

 

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

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

相关文章

代码随想录算法训练营Day13

110.平衡二叉树 力扣题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 后序迭代 class Solution {public boolean isBalanced(TreeNode root) {return getHeight(root)!-1;}public int getHeight(TreeNode root){if(rootnull){return 0;}int leftheightgetHei…

【python】函数的定义

目录 函数介绍函数的定义函数的参数函数的返回值函数说明文档函数的嵌套调用变量的作用域综合案例 学习目标&#xff1a; 掌握函数的基础定义语法 函数的定义 函数的定义&#xff1a; def 函数名(传入参数):函数体return 返回值函数的调用&#xff1a; 函数名(参数)演示…

WordPress精选文章如何添加侧边栏和页面?

WordPress精选帖子是一项功能&#xff0c;可让用户在其网站主页或其他值得注意的部分突出显示特定帖子。这些精选帖子通常以视觉上独特的方式显示&#xff0c;例如以滑块、网格或轮播格式显示&#xff0c;以提高其可见性和对访问者的吸引力。 网站所有者可以手动选择他们想要推…

云计算Openstack

OpenStack是一个开源的云计算管理平台项目&#xff0c;由美国国家航空航天局&#xff08;NASA&#xff09;和Rackspace公司合作研发并发起&#xff0c;以Apache许可证授权。该项目旨在为公共及私有云的建设与管理提供软件支持&#xff0c;通过一系列相互协作的组件实现云计算服…

MMD模型及动作一键完美导入UE5-衣服布料模拟(四)

1、给角色刷布料 1、打开角色,通过Window->Clothing打开模型布料窗口 2、选中裙子右键,创建布料数据 3、选择裙子,右键->应用布料数据 4、激活布料画笔,就可以开始绘制布料了 5、调整画笔大小和布料值进行绘制,布料值为0表示刚体

高校教师成果管理小程序的设计与实现springboot(lw+演示+源码+运行)

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对高校教师成果信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…

5分钟精通Excel在go中的使用

一些简单操作可以在官方文档中找到&#xff0c;应该足够无经验的朋友们入门 介绍 - 《Excelize v2.2 中文文档》 - 书栈网 BookStack 这里贴一个中文版的链接&#xff08;以excelize库为例&#xff0c;相对其他库来说&#xff0c;体验很不错&#xff09;&#xff0c;不过要注…

c++反汇编逆向还原——for循环(笔记)

c反汇编逆向还原代码for循环的实现&#xff0c;for循环和while循环在逆向还原的区别 一、汇编 mov &#xff1a;将源操作数复制到目的操作数 lea &#xff1a;与mov类似 mov a&#xff0c;b 表示将b赋值给a 若是 mov a&#xff0c;[b] 这是将b的地址赋值给a&#xff0c;相…

第一弹:llama.cpp编译

1.编译llama.cpp命令行&#xff08;电脑版本&#xff09;&#xff1b; 2.交叉编译安卓命令行版本。 一、Llama.cpp是什么&#xff1f; 二、Llama.cpp编译 首先我们尝试编译llama.cpp. 2.1 下载llama.cpp 项目的github地址&#xff1a; https://github.com/ggerganov/llama…

k8s pv(PersistentVolume) 状态含义

一、PV 状态含义 在Kubernetes中&#xff0c;PersistentVolume&#xff08;PV&#xff09;有几种不同的状态&#xff0c;这些状态反映了PV的生命周期和可用性。以下是主要的PV状态及其含义&#xff1a; Available: 表示该PV尚未被任何PersistentVolumeClaim (PVC)绑定&#xf…

Kafka系列之:安装部署CMAK,CMAK管理大型Kafka集群参数调优

Kafka系列之:安装部署CMAK,CMAK管理大型Kafka集群参数调优 一、CMAK二、要求三、配置四、启动服务五、使用 Security 启动服务六、消费者/生产者滞后七、从 Kafka Manager 迁移到 CMAK八、CMAK管理大型Kafka集群参数调优九、后台运行CMAK十、输出日志一、CMAK CMAK(之前称为…

Linux---文件io

1.系统调用 由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface&#xff0c;API)。是应用程序同系统之间数据交互的桥梁。 C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。 man手册中一共有九卷&#xff0c;其中一卷就有讲到系…

连锁店收银系统如何选择?

在新零售背景下&#xff0c;连锁店的收银系统扮演着至关重要的角色。随着科技的不断发展和消费者需求的不断变化&#xff0c;一款功能齐全的收银系统不仅可以提高便利店的运营效率&#xff0c;还可以提供更好的消费体验。以下是连锁店收银系统必备的功能。 1.收银系统能支持独…

【二十七】【QT开发应用】VS如何复制项目,QT无边窗窗口Pro版本,信号与信号槽的应用,背景图片自适应控件大小

VS复制项目 在使用VS的过程中,有的时候我们需要复制我们已经存在的项目. 我们可以先创建一个新的项目. 接着把需要复制的项目的文件复制粘贴到新的项目文件夹中. 不要忘记添加现有项目. CFrameLessWidgetBase.h #pragma once #include <QWidget> class CFrameLessWi…

书生大模型实战(从入门到进阶)L2-茴香豆:企业级知识库问答工具

目录 茴香豆介绍 茴香豆本地标准版搭建 环境搭建 配置服务器&#xff1a; 搭建茴香豆虚拟环境&#xff1a; 安装茴香豆 下载茴香豆 安装茴香豆所需依赖 下载模型文件 更改配置文件 知识库创建 测试知识助手 命令行运行 Gradio UI 界面测试 本文是对书生大模型L2-茴香…

SwiftUI简明概念(3):Path.addArc的clockwise方向问题

一、画个下半圆 SwiftUI中绘制下半圆的一个方法是使用Path.addArc&#xff0c;示例代码如下&#xff1a; var body: some View {Path { path inpath.addArc(center: CGPoint(x: 200, y: 370), radius: 50, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 180.0), …

自然语言处理实战项目:从基础到实战

自然语言处理实战项目&#xff1a;从基础到实战 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能的重要分支&#xff0c;致力于让计算机能够理解、生成和处理人类语言。NLP 在搜索引擎、智能客服、语音助手等场景中扮演着关键角色。本文将带…

不同领域的常见 OOD(Out-of-Distribution)数据集例子

以下是几个来自不同领域的常见 OOD&#xff08;Out-of-Distribution&#xff09;数据集例子&#xff0c;这些数据集常用于测试和研究模型在分布变化或分布外数据上的泛化能力&#xff1a; 1. 计算机视觉领域 CIFAR-10 vs. CIFAR-10-C / CIFAR-100-C: 描述&#xff1a;CIFAR-10…

MyBatis-Plus分页查询

在实际开发中&#xff0c;对于大量数据的查询&#xff0c;可以通过分页查询的方式来减少查询量和提高查询效率。在 MyBatis-Plus 中&#xff0c;分页查询可以通过使用 Page 对象和 IService 接口提供的分页方法来实现。MyBatis-Plus 的分页插件 PaginationInnerInterceptor 提供…

负载均衡--会话保持失败原因及解决方案(五)

会话保持失败可能由多种因素导致&#xff0c;以下是一些主要原因及其解释&#xff1a; 一、服务器及网络问题 服务器故障&#xff1a; 服务器出现故障或不稳定&#xff0c;导致无法正确处理会话信息。这可能是由于硬件故障、网络问题或软件错误等引起的。网络问题&#xff1a…