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 提供功能。应用程序无需或者也不应该直接引用接口来使用实现类(因为这些实现类的实例不是程序员手动实例化的,所以实现了解耦)。
notion image
其实这之间可以分为三个角色:
  1. 使用服务功能的使用方
  1. 定义服务接口并封装服务功能的服务接口提供方
  1. 具体服务实现的服务提供方
对于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。
  1. JDBC 4.0+ 通过SPI机制自动加载 数据库驱动(MySQL、MariaDB等),不再需要手动调用Class.forName()加载驱动。
  1. SLF4J 框架 通过SPI去绑定底层日志实现(Logback、Log4j2),日志接口统一,但实现可以不同。
当然 Java 提供的 SPI 缺点也是显而易见的:
  1. 一次就将所有的实现类都加载了,不能按需加载
  1. 原生的 SPI 不能热更新,系统在运行时更换和新增新的服务实现
  1. ServiceLoader 不是线程安全的,所以需要这个问题需要接口提供方去考虑了

至于具体ServiceLoader的实现,可以查看下面的参考文章

参考

 
随笔-2025-06-08随笔-2025-05-31
Loading...