SPI(Service Provider Interface) 服务提供发现接口。
不同于微服务中的服务发现,其本质是典型的面向接口编程,使用了策略模式,实现解耦。
同时SPI 使用的是一种 ”插件思维“,即服务提供者负责服务所有的维护,当替换服务提供方时不需要调用方修改代码及配置文件。
即我们只声明接口,具体的实现并不在程序中确定,而是由程序之外的配置掌控,用于具体实现的装配。
组成:
服务:指接口和抽象类的集合。由服务调用方提供
服务提供者:是服务的特定实现
服务提供者可以以扩展的形式安装在Java平台的实现中,也就是将 jar 文件放入任意常用的扩展目录中。也可通过将提供者加入应用程序类路径路径,或者通过其他某些特定于平台的方式使其可用。
SPI规范:
1、在资源目录 resource/META-INF/services 中放置提供者配置文件 来标识服务提供者。文件名称是服务类型(接口)的全限定名(最终会通过这里的全限定名来反射创建对象)。
2、该文件包含一个具体提供者类的全限定名,每行一个。忽略各名称周围的空格、制表符和空行。可以使用 ‘#’ 标注注释。
3、该文件必须使用UTF-8编码
服务加载器:
java.util.ServiceLoader<S>
JDK官方提供的SPI机制下的服务加载器,S:要被此加载器加载的服务类型
返回 | 方法名 | 描述 |
Iterator<S> | iterator() | 以延迟方式加载此加载器服务的可用提供者。 |
static <S> ServiceLoader<S> | load(Class<S> service) | 针对给定服务类型创建新的服务加载器,使用当前线程的上下文类加载器。 |
static <S> ServiceLoader<S> | load(Class<S> service, ClassLoader loader) | 针对给定服务类型和类加载器创建新的服务加载器。 |
static <S> ServiceLoader<S> | loadInstalled(Class<S> service) | 针对给定服务类型创建新的服务加载器,使用扩展类加载器。 |
void | reload() | 清除此加载器的服务者缓存,以重载所有服务者。 |
String | toString() | 返回一个描述此服务的字符串。 |
SPI具体步骤如下:
1、定义一个接口及对应的方法
2、编写该接口的一个实现类
3、在META-INF/services/ 目录下,创建一个以接口全路径命名的文件
4、文件内容为具体实现类的全路径名,每行一个
5、在代码中通过java.util.ServiceLoader 来加载具体的实现类
示例:
mysql-connector-java-5.1.47.jar为例
1、接口服务Driver,全限定名为:
java.sql.Driver
2、服务配置
在其jar包的META-INF/services/ 目录下有个 java.sql.Driver 文件,内容为:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
3、调用方使用ServiceLoader加载所有提供的服务 (java.sql.DriverManager中的static{loadInitialDrivers();})
Dubbo SPI:
http://dubbo.apache.org/docs/v2.7/dev/spi/