JavaSPI详解

目录

  • 一个问题
  • 什么是SPI
  • API 与 SPI
  • 一个简单的例子
  • SPI机制的实现
  • Java SPI的问题
  • 为什么SPI机制打破了双亲委派模型
  • 参考资料
一个问题【JavaSPI详解】在项目开发中 , 经常会使用到数据库驱动,我们连接的数据库可能是MySQL也有可能是Oracle,但是不管使用什么数据库都是引入数据库驱动配置相应的地址、用户、密码信息就可以使用而不用修改业务代码 。
这是因为在JDK中提供了一个java.sql.Driver接口 。各个数据库厂商只需要实现这个接口,当我们引入相应驱动,连接数据库的时候,就会使用厂商提供的实现 , 那么又是如何知道厂商实现的类路径的呢??
什么是SPISPI全名Service Provider interface,翻译过来就是“服务提供接口”,再说简单就是提供某一个服务的接口 ,  提供给服务开发者或者服务生产商来进行实现 。Java SPIJDK内置的一种动态加载扩展点的实现 。这个机制在一般的业务代码中很少用到(个人接触到的业务没有用到过),但是再底层框架中却被大量使用,包括JDBCDubboSpring框架、日志接口中都有用到,不同的是有的使用Java原生的实现 , 有的框架则自己实现了一套SPI机制
API 与 SPIAPI 全称Application Programming Interface,翻译为“应用程序接口” , 指的是应用程序为外部提供服务的接口,这个接口通常由服务提供者自行开发,定义好接口后很少改动 。APISPI示意图如图1 , 图2所示
JavaSPI详解

文章插图
图1  API示意图
JavaSPI详解

文章插图
图2  SPI示意图一般应用(模块)之间通过接口进行通讯 , 服务提供方提供接口并进行实现后,调用方就可以通过调用这个接口拥有服务提供发提供的能力,这个就是API当接口是由服务调用方提供,并且由服务提供方进行实现时,服务调用方就可以根据自己的需要选择特定实现,而不用更改业务代码以获取相应的功能 , 这个就是SPI
一个简单的例子这个功能是向注册中心注册服务的一个示例(过于简单的示例),
首先定义一个接口Registry, 这个接口只有一个功能,就是向注册中心注册一个服务,但是我现在并不确定我选的是什么注册中心,于是提供了一个统一的接口,由各个厂商进行实现
package cn.bmilk.chat.spi;public interface Registry {    void  registry(String host, int port);}厂商实现好后,需要在其META-INF/services文件夹下新增一个文件,文件名为该接口的全限定名即:cn.bmilk.chat.spi.Registry, 内容为接口实现的全限定名 , 这里我写了两个
cn.bmilk.chat.spi.EurekaRegistrycn.bmilk.chat.spi.ZookeeperRegistry两个类的都是空实现,内容如下
    @Override    public void registry(String host, int port) {        System.out.println(this + "registry , host = " + host +"  port = " + port);    }下面编写测试主类,通过 ServiceLoader 加载 Registry 实现
public class MainTest {    public static void main(String[] args) {        ServiceLoader<Registry> load = ServiceLoader.load(Registry.class);        Iterator<Registry> iterator = load.iterator();        while (iterator.hasNext()){            Registry registry = iterator.next();            registry.registry("127.0.0.1", 10086);        }    }}运行结果
class cn.bmilk.chat.spi.EurekaRegistrycn.bmilk.chat.spi.EurekaRegistry@12a3a380registry , host = 127.0.0.1  port = 10086class cn.bmilk.chat.spi.ZookeeperRegistrycn.bmilk.chat.spi.ZookeeperRegistry@29453f44registry , host = 127.0.0.1  port = 10086

推荐阅读