参考:
Java SPI的概念
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。
在jdk6里面引进的一个新的特性ServiceLoader,从官方的文档来说,它主要是用来装载一系列的service provider。而且ServiceLoader可以通过service provider的配置文件来装载指定的service provider。当服务的提供者,提供了服务接口的一种实现之后,我们只需要在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
jdk spi需要遵循的规范:
Demo代码
文件:/META-INF/services/com.example.demo.spi.HelloService
内容:
com.example.demo.spi.EnglishService
com.example.demo.spi.ChineseService
HelloService.java
package com.example.demo.spi;
public interface HelloService {
void sayHello();
}
EnglishService.java
package com.example.demo.spi;
public class EnglishService implements HelloService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
ChineseService.java
public class ChineseService implements HelloService {
@Override
public void sayHello() {
System.out.println("你好");
}
}
Test.java
package com.example.demo.spi;
import java.util.ServiceLoader;
public class Test {
public static void main(String[] args) {
ServiceLoader<HelloService> services = ServiceLoader.load(HelloService.class);
for (HelloService service : services) {
service.sayHello();
}
}
}
输出结果:
其他案例:
JDBC
在JDBC4.0之前,我们开发有连接数据库的时候,通常会用Class.forName(“com.mysql.jdbc.Driver”)这句先加载数据库相关的驱动,然后再进行获取连接等的操作。
而JDBC4.0之后不需要用Class.forName(“com.mysql.jdbc.Driver”)来加载驱动,直接获取连接就可以了,现在这种方式就是使用了Java的SPI扩展机制来实现。
JDBC接口定义
首先在java中定义了接口java.sql.Driver,并没有具体的实现,具体的实现都是由不同厂商来提供的。
mysql实现
在mysql的jar包mysql-connector-java-6.0.6.jar中,可以找到META-INF/services目录,该目录下会有一个名字为java.sql.Driver的文件,文件内容是com.mysql.cj.jdbc.Driver,这里面的内容就是针对Java中定义的接口的实现。
postgresql实现
同样在postgresql的jar包postgresql-42.0.0.jar中,也可以找到同样的配置文件,文件内容是org.postgresql.Driver,这是postgresql对Java的java.sql.Driver的实现。
org.postgresql.Driver
SPI的使用步骤总结
看完上面的数据库驱动的解析,应该都能知道大概的流程了:
- 有关组织或者公司定义标准。
- 具体厂商或者框架开发者实现。
- 程序猿使用。
有关组织或者公司定义标准。
具体厂商或者框架开发者实现。
程序猿使用。
ServiceLoader注意事项
ServiceLoader不是实例化以后,就去读取配置文件中的具体实现,并进行实例化。而是等到使用迭代器去遍历的时候,才会加载对应的配置文件去解析,调用hasNext方法的时候会去加载配置文件进行解析,调用next方法的时候进行实例化并缓存。
所有的配置文件只会加载一次,服务提供者也只会被实例化一次,重新加载配置文件可使用reload方法。