type
status
date
slug
summary
tags
category
icon
password
SPI是什么?
SPI(Service Provider Interface)即服务提供接口,这是Java提供的一个接口和实现解耦的方式,使得服务的实现可以以插件的方式加入或者抽离于原系统,而原系统并不做改变。
我们可以想象这样一个场景,系统针对某一个功能定义了接口,然后开发了相应的实现,但是后续需求发生变化,需要重新开发相应的实现或者新增其他实现。而且原系统同样需要做相应的适配,并重新发布版本。这样十分麻烦,也没有遵守开闭原则,那么就需要一种方式在不修改原系统的情况下,更换或者新增实现。
SPI是怎么做的呢?这个接口无非就是提供了一个功能,实际上使用方并不关心具体的实现,也不需要知道具体类长什么样子,那么只要提供一个类(
M),这个类统一提供使用方所需要的功能就行,至于这个类要怎么获得具体的功能,这就是 Java 提供的一个功能,利用ServiceLoader 固定加载 CLASSPATH 下 META-INF/services目录中定义的文件,从中获取实现类的 classname,然后通过类加载器加载所有实现类,并且实例化。这些实现类都实现了接口中定义的功能,然后同一通过 M 提供功能。应用程序无需或者也不应该直接引用接口来使用实现类(因为这些实现类的实例不是程序员手动实例化的,所以实现了解耦)。
其实这之间可以分为三个角色:
- 使用服务功能的使用方
- 定义服务接口并封装服务功能的服务接口提供方
- 具体服务实现的服务提供方
对于JDK或者一些框架来说,他们通常定义出服务的接口,然后给出一些实现(当然这取决于哪种服务),然后开发者使用他们封装好的服务类(
M)来实现业务功能,但是具体需要哪种服务的实现,则是开发者自己从服务提供方选择(或自己实现)。而且以后修改或者新增实现,只需要提供一个实现了功能的 Jar 包放在应用程序的 classpath 下,重新启动程序就可以了。SPI的使用 DEMO
1. 定义服务的接口
首先定义需要服务提供方提供的哪些能力,也就是服务提供方需要实现的接口。
2. 接口提供方使用 ServiceLoader 加载服务的实现
通常使用方不应该去主动使用
ServiceLoader去加载服务的实现,而是接口提供方帮忙加载并且管理,暴露给使用方的只是相应的服务。当然这部分具体怎么做,应该是接口提供方去考虑具体考虑。另外,接口提供方也可以自己去实现基本的一个服务,相当于自给自足了,但这样保持了开闭原则,别人也可以进行扩展。
接口提供方,需要在提供的 Jar 包中也定义好自己提供的默认实现,在 resources 目录下 的META-INF/services 添加相应的文件
这里需要添加的文件需要按照规定的格式才会生效:
文件名:接口的全限定名称(
com.demo.spi.service.HelloService)文件内容:接口实现的全限定名称(
com.demo.spi.service.DefaultHelloService)3. 服务提供方实现接口
如果不想使用默认实现就可以自己实现接口,提供自己想要的实现,具体做法如下:
引入接口提供方提供的依赖
实现接口
在 META-INF/services 下添加文件
文件名:com.demo.spi.service.HelloService
Package 成 Jar 包提供出去
将实现的服务以 Jar 包的方式提供出去即可。
4. 使用方引入服务接口和服务实现
引入服务依赖(一般服务实现方中会依赖接口,但是最好是显式给出版本,好对版本进行控制)
使用相应的服务
使用方只需要更换或新增服务实现的依赖就可以更新或扩展功能了。
源码可以查看:
应用场景
SPI 在 JDK 中很多地方都有使用(只要反调一下
ServiceLoader就知道了),而且也有其他的框架使用了SPI提供服务,比如:日志框架 SLF4J。- JDBC 4.0+ 通过SPI机制自动加载 数据库驱动(MySQL、MariaDB等),不再需要手动调用Class.forName()加载驱动。
- SLF4J 框架 通过SPI去绑定底层日志实现(Logback、Log4j2),日志接口统一,但实现可以不同。
当然 Java 提供的 SPI 缺点也是显而易见的:
- 一次就将所有的实现类都加载了,不能按需加载
- 原生的 SPI 不能热更新,系统在运行时更换和新增新的服务实现
ServiceLoader不是线程安全的,所以需要这个问题需要接口提供方去考虑了
至于具体
ServiceLoader的实现,可以查看下面的参考文章参考
- 作者:小旭
- 链接:http://notion.rookiexu.cn/article/java-spi
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。


