Java SPI机制,你了解过吗?
Life moves pretty fast,if you don't stop and look around once in a while,you will miss it
为什么需要SPI?
思考一个场景,我们封装了一套服务,别人通过引入我们写好的包,就可以使用这些接口API,完成相应的操作,这本来没有什么问题,但是会存在使用该服务的实体有不相同的业务需求,需要进一步的扩展,但是由于api是写好的,想要扩展并非那么的简单,如果存在这样子的场景,我们该怎么办?
可以使用Java 提供的SPI机制
什么是SPI?SPI和API的区别
SPI
SPI的全称是
Service Provider Interface,是Java提供的可用于第三方实现和扩展的机制,通过该机制,我们可以实现解耦,SPI接口方负责定义和提供默认实现,SPI调用方可以按需扩展API的全称是
Application Programming Interface,广义上来看就是接口,负责程序与程序之间进行协作的通道,就好比上面给的例子,【我们封装好了一套服务,通过API的形式提供给他人使用,别人使用API就能得到想要的】
所以他们俩的区别就很明显了,API的调用方只能依赖使用提供方的实现,SPI就如同可定制化的API一样,调用方可以自定义实现替换API提供的默认实现
来人,上点对抗
首先,我们新建一个空的maven项目,里边有两个包
- spi-provider 从名字就可以得知是SPI的提供方
- spi-user SPI的使用方

spi-provider
我们简单定义一个SPI接口,就叫ISpiTest,里边就一个saySomething方法,再提供一个默认的实现
public interface ISpiTest {
void saySomething();
}
public class DefaultSpiImplementation implements ISpiTest{
@Override
public void saySomething() {
System.out.println("[默认实现] -> 今天也是充满希望的一天");
}
}
然后,模拟走流程,注意步骤4是我们之后要自定义替换的
/**
* 模拟一套流程
* @author Amg
* @date 2021/12/9
*/
public class TestUtils {
public static void workFlow(ISpiTest s) {
System.out.println("1、步骤1.......");
System.out.println("2、步骤2.......");
System.out.println("3、步骤3.......");
System.out.print("4、步骤4:");
s.saySomething();
System.out.println("5、步骤5.......");
}
}
接着,重点来了,我们需要在resources目录下面创建/META-INF/services文件夹,然后以SPI接口的全限定类名作为名称创建一个文件

往文件里面填写实现类的全限定类名,如下
com.amg.spi.DefaultSpiImplementation
到此,spi-provider这个模块就完成了,至于之后要怎么使用,到spi-user模块中进一步说明
spi-user
首先,我们在pom文件中,引入spi-provider坐标依赖

然后定义main方法,在main方法中调用在spi-provider中定义的SPI接口,此时采用的是默认的配置

可以注意到我们使用ServiceLoader这个类的load方法,传入SPI接口的字节码进行构造,我们在spi-provider中resources中给出了一个默认实现,但是我们是在spi-user中去调用的,ServiceLoader会自动读取META-INF下的配置文件,就算是跨jar包也是可以的
然后现在我们在spi-user中定义一个实现类,以及把他配置到META-INF下(需要注意,这个配置的全限定类名仍然需要是spi-provider中定义SPI接口的路径),来看看效果

spi-user下META-INF里边内容如下
com.amg.spiuser.service.impl.WantHamburger

可以发现,我们并没有改变任何的客户端代码,只是把配置文件进行了简单的修改,即可完成自定义实现,这就是使用SPI的魅力
思考一下,我们之前的流程是怎么做的
- 首先定义了一个接口,面向接口编程嘛
- 定义配置文件
- 各个自定义的实现类,只需要按照规则重写配置文件即可
总结
通过这个流程,我们可以归纳为一句话,SPI是策略模式的一种体现,配合面向接口编程的思想以及必要的配置文件,即可完成定义和具体实现的解耦,而且是可定制化的API
SPI的优点有以下
- 定制化实现接口
- 解耦
SPI的缺点有以下
- 通过观察ServiceLoader,可以发现并没有额外的加锁机制,所以会存在并发问题
- 获取对应的实现类不够灵活,从上面例子可以看出,需要使用迭代器的方式获取
- 需要知道接口的所有具体实现类,所以每次都要加载和实例化所有的实现类
实际中,SPI的使用还是很常见的,例如Dubbo和Spring Boot都为我们提供了一套SPI机制,只不过此SPI是在Java提供的SPI机制基础上进行改造而来,有兴趣的同学也可以去查下资料,增长增长
好啦,本期的文章就到这里,限于本人水平的问题,如有写得不对的地方,欢迎指出更正,谢谢!
Java SPI机制,你了解过吗?的更多相关文章
- Java spi机制浅谈
最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现和接入都采用了java的spi机制. 所以简单的总结下java spi机制的思想. 我们系统里抽象的各个模块,往往有很多不同的实现方案,比 ...
- JDK源码解析之Java SPI机制
1. spi 是什么 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件. 系统设计的各个抽象,往往 ...
- 聊聊Java SPI机制
一.Java SPI机制 SPI(Service Provider Interface)是JDK内置的服务发现机制,用在不同模块间通过接口调用服务,避免对具体服务服务接口具体实现类的耦合.比如JDBC ...
- Java SPI机制实战详解及源码分析
背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...
- 组件化框架设计之Java SPI机制(三)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 本篇文章将从深入理解java SPI机制来介绍组件化框架设计: ...
- Java SPI 机制实现解耦与本地化
SPI 是 Java 提供的一种服务加载方式,全名为 Service Provider Interface,可以避免在 Java 代码中写死服务的提供者,而是通过 SPI 服务加载机制进行服务的注册和 ...
- Java SPI机制详解
Java SPI机制详解 1.什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个 ...
- java SPI机制
1. SPI是Service Provider Interfaces的简称.根据Java的SPI规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即Service Provider(服务 ...
- Java SPI机制学习笔记
最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口. JavaSPI 实际上是“基于接口的编程+策略模式+配置文 ...
- Java SPI机制简介
SPI 简介 SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现 ...
随机推荐
- ES6基础知识(Generator 函数应用)
1.Ajax 是典型的异步操作,通过 Generator 函数部署 Ajax 操作,可以用同步的方式表达 function* main() { var result = yield request(& ...
- 跟着老猫来搞GO-容器(1)
前期回顾 前面的一章主要和大家分享了GO语言的函数的定义,以及GO语言中的指针的简单用法,那么本章,老猫就和大家一起来学习一下GO语言中的容器. 数组 数组的定义 说到容器,大家有编程经验的肯定第一个 ...
- [hdu6984]Tree Planting
构造一个01矩阵,其中格子$(i,j)$对应于第$ik+j$个的位置(其中$0\le i<\lceil\frac{n}{k}\rceil,0\le j<k$,位置 ...
- [loj3364]植物比较
结论:设$b_{i}$满足该限制,则$a_{i}$合法当且仅当$\forall i\ne j,a_{i}\ne a_{j}$且$\forall |i-j|<k,[a_{i}<a_{j}]= ...
- Insights直播回顾,对话专家,HMS Core 6创新能力解读
HMS Core Insights第八期直播–对话专家,HMS Core 6创新能力解读,已于11月25日圆满结束,本期直播与小伙伴们一同了解了HMS Core 6在图形.媒体以及连接与通信领域推出的 ...
- .net core 3.1 WebAPi 使用 AutoMapper 9.0、10.0
AutoMapper 可以很方便完成数据对象之间的转换. Dto -> Entity Entity -> ViewModel Step 1:通过 NuGet 安装 AutoMapper 的 ...
- sb 错误
数组开小.很容易 \(2 \times 10^5\) 或 \(10^6\) 就开成 \(10^5\),或者各种变量的数据范围混用,\(m \leq 5\times 10^5\),结果只开到了 \(n\ ...
- Codeforces 526G - Spiders Evil Plan(长链剖分+直径+找性质)
Codeforces 题目传送门 & 洛谷题目传送门 %%%%% 这题也太神了吧 storz 57072 %%%%% 首先容易注意到我们选择的这 \(y\) 条路径的端点一定是叶子节点,否则我 ...
- Dirichlet 前缀和的几种版本
[模板]Dirichlet 前缀和 求 \[B[i] = \sum_{d|i} A[d] \] $ n \le 2\times 10^{7} $ 看代码: for( int i = 1 ; i < ...
- perl练习——计算点突变
题目来源:http://rosalind.info/problems/hamm/ 一.程序目的:计算序列点突变(Point Mutations) 输入: GAGCCTACTAACGGGAT CATCG ...