Java SPI机制

SPI即Service Provider Interface,定义java组件及调用规范,是一种程序组件化方案。

Java组件一个更为人熟知的名词“服务”,与流行的微服务有所区别。

以往加载JDBC驱动使用如下方式:

Class.forName()

Java 1.6引入SPI机制,使用DriverManager创建数据连接,背后是SPI机制保证组件正确加载。

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

SPI

以JDBC为例说明SPI工作的原理。

Service Provider Interface

java.sql.Driver

定义驱动程序行为。SPI中此类被称为Service Provider Interface。

Service Provider

com.mysql.cj.jdbc.Driver

Driver的具体实现。SPI中此类被称为Service Provider。

配置文件

SPI要求Service Provider实现Service Provider Interface时,明确标注服务入口。

即,在jar包中用一个文件将程序入口类列出来,该文件即配置。文件名及位置都有明确要求。

位置

META-INF/services

文件

名称:Service Provider Interface全类名

内容:Service Provider全类名

如下图所示:

加载机制/Service Loader

SPI服务调用相当简单,将服务jar包引入工程,使用ServiceLoader加载服务,如:

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

ServiceLoader在classpath下搜索spi配置文件。

DriverManager加载驱动后,只需要一行代码就能获取JDBC连接。

Connection conn = DriverManager.getConnection(url,username,password);

四个主要组件

服务提供者接口:服务具有的行为。

服务提供者:服务具体实现。

SPI配置文件:用于配置服务入口。

ServiceLoader:加载具体服务实现。

ServiceLoader方法

load():

加载SPI服务的静态方法。

findFirst():

从众多服务提供者中选一个。

forEach():

遍历服务接口下的所有服务提供者。

reload():

重新加载服务提供者。

SPI应用

Java提供了众多SPI,以下是接口及服务:

CurrencyNameProvider:为 Currency类提供本地化的货币符号。

LocaleNameProvider:为 Locale类提供本地化名称。

TimeZoneNameProvider:为 TimeZone类提供本地化的时区名称。

DateFormatProvider:提供指定区域设置的日期和时间格式。

NumberFormatProvider:为 NumberFormat类提供货币,整数和百分比值。

Driver:从4.0版开始,JDBC API支持SPI模式。

PersistenceProvider:提供JPA API的实现。

JsonProvider:提供JSON处理对象。

JsonbProvider:提供JSON绑定对象。

Extension:为CDI容器提供扩展。

ConfigSourceProvider:提供检索配置属性的源。

Spring Boot组件集成也依赖这一思想,不过spring有自己的实现。