Java SPI机制 - 示例解说

Java SPI定义了组件提供者和调用者应遵循的规范,是Java提供的一种程序组件化方案,SPI即Service Provider Interface。

在Java中,组件有一个更为人熟知的名词“服务”,但与现在流行的微服务有所区别。在以往加载JDBC驱动使用Class.forName()方法,在Java 1.6引入SPI机制后,可以使用DriverManager直接创建链接,背后就是SPI机制保证了组件的正确加载。

Java SPI

以JDBC为例说明Java SPI如何定义服务,并保证服务可用的。

Service Provider Interface

Java在java.sql.Driver中定义了驱动程序应具备的行为,但并没有具体的实现。在SPI中java.sql.Driver这类接口,被称为Service Provider Interface。

Service Provider

MySQL根据java.sql.Driver的要求,实现了自已的驱动程序com.mysql.cj.jdbc.Driver。在SPI中jcom.mysql.cj.jdbc.Driver这类实现,被称为Service Provider。

以上做法在SPI没有出现之前也是这么玩的,接下来看看SPI具体做的实质性工作。

SPI配置文件

SPI要求Service Provider在实现Service Provider Interface时,要明确标注服务的入口,即,在jar文件中用一个文件把程序的入口类列出来。文件的位置和命名都有明确要求,在根目录的META-INF/services目录下,创建一个文件,文件名是Service Provider Interface的全类名,内容是Service Provider的全类名。

以上就SPI服务提供者所要做的全部工作,接下来由SPI加载机制完成。

ServiceLoader

对实现了SPI标准的服务,使用就相当的简单了,把服务提供者的jar文件引入工程,使用ServiceLoader加载服务接口就可以了。如:

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

ServiceLoader会在classpath下搜索约定的文件,就可以找到服务提供者的入口类了。

实际上在使用DriverManager加载驱动时,DriverManager就使用以上代码查找驱动程序的,所以,只需要一行代码就能获取到JDBC连接。

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

Java SPI四个主要组件

服务提供者接口:定义服务所应具备的功能。

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

SPI配置文件:一种约定格式的文件,用于查找服务实现的入口。文件必须存在于META-INF/services目录中。文件名是服务提供者接口的全类名,文件内容是服务实现类的全类名。

ServiceLoader:Java SPI主类,用于加载服务提供者的服务。

ServiceLoader重要方法

load():加载特定SPI服务的静态方法。

findFirst():从众多服务提供者中选一个。

forEach():遍历服务接口下的所有服务提供者。

reload():重新加载服务提供者。

Java 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有自己的实现。