何谓SPI?
SPI 即Service Provider Interface,服务提供者的接口,专门提供给服务提供者或者框架功能的开发者去使用的一个接口。很多框架都使用到了SPI,数据库加载驱动、日志接口等
SPI和API有什么区别
- SPI 中的接口,是服务调用方设计接口规则,由实现方去实现接口。比如日志面板slf4j,具体日志框架是logback、log4j还是log4j2并不关心,slf4j设计的接口规则,由具体日志框架去实现即可
- API中的接口,是服务提供方设计并且实现的,一般前后端或者微服务之间调用的就是API。

浅析原理
其实SPI中最重要的是其中两个服务,一个是接口调用方(Slf4j),一个是接口实现方(logback)。下面举例slf4j和logbak的SPI实现原理
- Slf4j作为日志门面,规范了日志的实现规则,日志框架需要按照此规则才能接入slf4j日志门面。
https://github.com/qos-ch/slf4j/blob/master/slf4j-api/src/main/java/org/slf4j/spi/SLF4JServiceProvider.java
在该项目中,可以看到其中org.slf4j.spi.SLF4JServiceProvider 为制定的接口规则:

logbak作为日志框架,需要接入slf4j日志门面,就需要实现slf4j制定的接口规范。
需要在META-INF的service目录下,新建一个slf4j待实现接口的目录,并且内容为logback框架实现接口的文件路径:

可以看到logback对其接口进行了实现:

ServiceLoader
如上原理,当使用slf4j的时候,他怎么知道该使用logback还是log4j还是log4j2 ? 最关键的就是有一个服务调用方加载实现方的地方:ServiceLoader

调用方主要的流程就是:
- 通过 URL 工具类从 jar 包的
/META-INF/services目录下面找到对应的文件, - 读取这个文件的名称找到对应的 spi 接口,
- 通过
InputStream流将文件里面的具体实现类的全类名读取出来, - 根据获取到的全类名,先判断跟 spi 接口是否为同一类型,如果是的,那么就通过反射的机制构造对应的实例对象,
- 将构造出来的实例对象添加到
Providers的列表中。