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的魅力
:thinking:思考一下,我们之前的流程是怎么做的
- 首先定义了一个接口,面向接口编程嘛
- 定义配置文件
- 各个自定义的实现类,只需要按照规则重写配置文件即可
总结
通过这个流程,我们可以归纳为一句话,SPI是策略模式的一种体现,配合面向接口编程的思想以及必要的配置文件,即可完成定义和具体实现的解耦,而且是可定制化的API
SPI的优点有以下
- 定制化实现接口
- 解耦
SPI的缺点有以下
- 通过观察ServiceLoader,可以发现并没有额外的加锁机制,所以会存在并发问题
- 获取对应的实现类不够灵活,从上面例子可以看出,需要使用迭代器的方式获取
- 需要知道接口的所有具体实现类,所以每次都要加载和实例化所有的实现类
实际中,SPI的使用还是很常见的,例如Dubbo和Spring Boot都为我们提供了一套SPI机制,只不过此SPI是在Java提供的SPI机制基础上进行改造而来,有兴趣的同学也可以去查下资料,增长增长
好啦,本期的文章就到这里,限于本人水平的问题,如有写得不对的地方,欢迎指出更正,谢谢!
- 你应该了解的 Java SPI 机制
- Java的SPI机制与简单的示例
- java中的SPI机制
- java SPI机制详解
- 简单了解java等待唤醒机制原理及使用
- 全面了解:java网络编程的通信机制
- Java中的ClassLoader和SPI机制
- Javasssist初探(需要对Java的ClassLoader机制有一些了解)
- 你必须了解的java内存管理机制(二)-内存分配
- Java类的加载过程以及SPI机制
- 通过例子了解java的反射机制
- Java SPI 机制实现解耦与本地化
- java中的SPI机制
- 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理
- Java的Spi机制心得
- java 的SPI机制
- Java spi机制浅谈
- JavaSPI机制学习笔记
- 想了解JAVA内存机制以及内存泄漏情况的同行
- 深入学习Java中的SPI机制