在上一篇 gRPC源码剖析-Server启动流程 有提到过SPI机制,SPI对于大多数业务开发人员可能并不熟悉,但是在各底层基础框架中用得还是比较多的,今天我们来详细了解一下。
一、SPI机制
SPI,全称是Service Provider Interface,就是为某个接口寻找服务实现的机制。
SPI机制遵循约定
1、服务提供方提供接口具体实现,并且必须在Jar包的META-INF/services目录下创建以“接口全名”为文件名,内容为实现类全类名。
2、主程序通过ServiceLoader动态加载实现类,需要扫描META-INF/services目录下配置文件找到实现类然后反射加载到JVM。
二、ServiceLoader
ServiceLoader和ClassLoader差不多也是用来加载类文件,但和ClassLoader有一些区别
1、ServiceLoader加载的是有共同接口的实现类,ClassLoader是个万能类加载器。
2、ServiceLoader加载类时依赖于META-INF/services的配置。
3、ServiceLoader实现了Interator接口,方便遍历出接口对应所有的实现类。
4、ServiceLoader类内部引用了ClassLoader进行类加载。
重要方法:ServiceLoader<S> load(Class<S> service),为给定的接口创建类加载器并通过反射加载具体的实现类。
三、一个打印例子
创建一个Maven多模块应用
1、api模块: 接口定义
2、yunda模块:韵达打印面单实现
配置文件
路径:/resources/META-INF/
service/org.api.PrintService
内容:org.yunda.YundaPrintServiceImpl
3、zto模块:中通打印实现
配置文件
路径:/resources/META-INF/
service/org.api.PrintService
内容:org.yunda.ZtoPrintServiceImpl
4、test模块:测试
必须引入 zto包依赖或yunda包依赖,否则虽然编译时不会报错,但运行时加载不到类的实现,这里折腾了很久。
四、JDBC使用SPI分析
1、驱动
mysql-connector-java.jar
META-INF/service/java.sql.Driver内容com.mysql.cj.jdbc.Driver
ojdbc.jar
META-INF/service/java.sql.Driver内容oracle.jdbc.OracleDriver
2、DriverManager
DriverManager是JDBC里管理不同数据库驱动的工具,核心与SPI有关的代码如下
根据注入的jdbc.drivers加载对应的数据库驱动。
注:SPI在底层框架使用的还是极其广泛的,除上上面讲到的JDBC数据库驱动,还有Spring、Dubbo、gRPC甚至淘宝交易平台早期也是用SPI来做业务隔离。