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.rhkb.cn/news/434629.html

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

相关文章

关系模型与关系代数——数据库原理 总结2

2.1 关系模型 关系数据结构 关系模型的数据结构是二维表&#xff0c;亦称为关系。关系数据库是表的集合&#xff0c;即关系的集合。表是一个实体集&#xff0c;一行就是一个实体&#xff0c;它由有关联的若干属性的值所构成。 关系模型的相关概念 列就是数据项 或 字段 或 属…

基于SpringCloud的微服务架构下安全开发运维准则

为什么要进行安全设计 微服务架构进行安全设计的原因主要包括以下几点&#xff1a; 提高数据保护&#xff1a;微服务架构中&#xff0c;服务间通信频繁&#xff0c;涉及到大量敏感数据的交换。安全设计可以确保数据在传输和存储过程中的安全性&#xff0c;防止数据泄露和篡改。…

Study--Oracle-09--部署Openfiler存储服务器

免费的存储服务器软件有FreeNAS 和 Openfiler。 其中Freenas的网站上只有i386及amd64的版本,也就是说Freenas不能支持64位版本的Intel CPU,而Openfiler则提供更全面的版本支持,在其网站上可以看到支持多网卡、多CPU,以及硬件Raid的支持,还有10Gb网卡的支持。 Openfiler能把…

【RocketMQ】RocketMQ发送不同类型消息

&#x1f3af; 导读&#xff1a;本文介绍了RocketMQ消息队列系统中的几种消息发送模式及其应用场景&#xff0c;包括同步消息、异步消息以及事务消息。同步消息确保了消息的安全性&#xff0c;但牺牲了一定的性能&#xff1b;异步消息提高了响应速度&#xff0c;适用于对响应时…

演示:基于WPF的DrawingVisual开发的频谱图和律动图

一、目的&#xff1a;基于WPF的DrawingVisual开发的频谱图和律动图 二、效果演示 波形图 极坐标 律动图极坐标图 律动图柱状图 Dock布局组合效果 三、环境 VS2022,Net7,Win10&#xff0c;NVIDIA RTX A2000 四、主要功能 支持设置起始频率&#xff0c;终止频率&#xff0c;中心…

【HTTP 和 HTTPS详解】3

HTTP 状态代码 HTTP 状态代码是服务器发送给客户端的三位数字&#xff0c;用于指示客户端请求的结果。它们分为五类&#xff1a;信息性&#xff08;100-199&#xff09;、成功&#xff08;200-299&#xff09;、重定向&#xff08;300-399&#xff09;、客户端错误&#xff08…

【移植】Combo解决方案之W800芯片移植案例

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 本方案基于 OpenHarmony LiteOS-M 内核&#xff0c;使用联盛德 W80…

[论文精读]Membership Inference Attacks Against Machine Learning Models

中文译名&#xff1a;针对机器学习模型的成员推理攻击 会议名称&#xff1a;2017 IEEE Symposium on Security and Privacy (SP) 发布链接&#xff1a;Membership Inference Attacks Against Machine Learning Models | IEEE Conference Publication | IEEE Xplore CODE:Git…

【计算机网络】Tcp报文的组成,Tcp是如何实现可靠传输的?

Tcp的报文组成 TCP&#xff08;传输控制协议&#xff09;是计算机网络中一种重要的传输协议&#xff0c;其报文组成包括多个字段&#xff0c;每个字段具有特定的含义。以下是TCP报文头的主要组成部分&#xff1a; 源端口号&#xff08;Source Port&#xff09;&#xff1a;占用…

C语言 | Leetcode C语言题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){int stack1[100];int stack2[100];int top1 0;int top2 0;int carry 0;int sum 0;struct ListNode* temp NULL;struct ListNode* head NULL;while (l1) {…

有关若依菜单管理的改造

导言&#xff1a; 搞了个后端对接若依前端&#xff0c;对接菜单管理时候懵懵的就搞完了&#xff0c;也是搞了很久。记一下逻辑和要注意的东西&#xff0c;以后做想似的能有个改造思路。 逻辑&#xff1a; 主要是要把后端传过的数组列表做成类似 这样的&#xff0c;所以要转格式…

git工具指令

下面是常用的Git命令清单&#xff0c;几个专用名称的译名如下&#xff1a; Workspace &#xff1a;工作区 Index /Stage&#xff1a;暂存区 Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09; Remote&#xff1a;远程仓库新建代码库 在当前目录新建一个Git代…

如何在银河麒麟操作系统中查看内存页大小

如何在银河麒麟操作系统中查看内存页大小 1、操作步骤2、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在操作系统中&#xff0c;内存页大小&#xff08;Page Size&#xff09;是一个重要的概念&#xff0c;它决定了操作系统如何…

GPT理论

1.GPT发展 Transformer是一个用作翻译任务的模型&#xff0c;谷歌出品。 GPT全称 lmproving Language Understanding by Generative Pre-Training&#xff0c;用预训练语言理解模型。OPENAI出品。 BERT全称Pre-training of Deep BidirectionalTransformers for Language Unde…

深度学习反向传播-过程举例

深度学习中&#xff0c;一般的参数更新方式都是梯度下降法&#xff0c;在使用梯度下降法时&#xff0c;涉及到梯度反向传播的过程&#xff0c;那么在反向传播过程中梯度到底是怎么传递的&#xff1f;结合自己最近的一点理解&#xff0c;下面举个例子简单说明&#xff01; 一、…

828华为云征文|部署个人知识管理系统 SiyuanNote

828华为云征文&#xff5c;部署个人知识管理系统 SiyuanNote 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 SiyuanNote3.1 SiyuanNote 介绍3.2 SiyuanNote 部署3.3 Siyua…

WebSocket实现在线聊天室

项目实现源码&#xff1a; 前端源码 后端源码 1.常见的消息推送方式 1.1 轮询 1.1.1 轮询的概念 客户端以固定的事件间隔&#xff08;例如每秒或几分钟&#xff09;向服务器发送HTTP请求&#xff0c;服务器收到请求后&#xff0c;处理请求并返回数据给客户端 轮询具体实现htt…

element-plus中日历组件设置起始为周一

问题描述 element-plus中的日历组件默认是周日到周六&#xff0c;因业务需求&#xff0c;需要实现从周一到周日的顺序。 解决方式 引入dayjs及本地语言包&#xff0c;使用本地时区即可。 import dayjs from dayjs import dayjs/locale/zh-cn ... // 这一句是为了让日历使用本地…

Android 利用OSMdroid开发GIS

1、地址 Github地址&#xff1a;https://gitee.com/mirrors/osmdroid Git地址&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 Git下载包地址&#xff1a;Releases osmdroid/osmdroid GitHub 新建项目 osmdroid在线&#xff1a; &#xff08;1&#xff09…

基于Hive和Hadoop的图书分析系统

本项目是一个基于大数据技术的图书分析系统&#xff0c;旨在为用户提供全面的图书信息和深入的图书销售及阅读行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以…