(4) java的spi

2018-12-08  本文已影响0人  Mrsunup

1.JAVA 的SPI机制

SPI全称(service provider interface),是JDK内置的一种服务提供发现机制,目前市面上有很多框架都是用它来做服务的扩展发现,大家耳熟能详的如JDBC、日志框架都有用到;

简单来说,它是一种动态替换发现的机制。举个简单的例子,如果我们定义了一个规范,需要第三方厂商去实现,那么对于我们应用方来说,只需要集成对应厂商的插件,既可以完成对应规范的实现机制。 形成一种插拔式的扩展手段

2.实现一个SPI机制

spi的流程图.png
/**
 * @Project: 3.DistributedProject
 * @description:  模拟数据库的驱动
 * @author: sunkang
 * @create: 2018-06-30 19:33
 * @ModificationHistory who      when       What
 **/
public interface DataBaseDriver {
    String connect(String url);
}
/**
 * @Project:  java-spi
 * @description:   模拟MysqlDriver的驱动
 * @author: sunkang
 * @create: 2018-06-30 19:34
 * @ModificationHistory who      when       What
 **/
public class MysqlDriver implements DataBaseDriver {
    @Override
    public String connect(String url) {
        return "mySql driver : "+url;
    }
}
com.java.spi.MysqlDriver
/**
* @Project: java-spi
* @description:  应用的启动的例子
* @author: sunkang
* @create: 2018-06-30 19:33
* @ModificationHistory who      when       What
**/
public class JavaSpiDemo
{
   public static void main( String[] args )
   {
       ServiceLoader<DataBaseDriver> serviceLoader =ServiceLoader.load(DataBaseDriver.class);
       for(DataBaseDriver dataBaseDriver :serviceLoader){
           System.out.println(dataBaseDriver.connect("com.sunkang "));
       }
   }
}
mySql driver : com.sunkang

3.SPI规范总结

实现SPI,就需要按照SPI本身定义的规范来进行配置,SPI规范如下
1.需要在classpath下创建一个目录,该目录命名必须是:META-INF/services
2.在该目录下创建一个文件,该文件需要满足以下几个条件

3.通过java.util.ServiceLoader的加载机制来发现

4.SPI的实际应用

SPI在很多地方有应用,大家可以看看最常用的java.sql.Driver驱动。JDK官方提供了java.sql.Driver这个驱动扩展点,但是你们并没有看到JDK中有对应的Driver实现。 那在哪里实现呢?

以连接Mysql为例,我们需要添加mysql-connector-java依赖。然后,你们可以在这个jar包中找到SPI的配置信息。如下图,所以java.sql.Driver由各个数据库厂商自行实现。这就是SPI的实际应用。当然除了这个意外,大家在spring的包中也可以看到相应的痕迹


5.SPI的缺点

1.JDK标准的SPI会一次性加载实例化扩展点的所有实现,什么意思呢?就是如果你在META-INF/service下的文件里面加了N个实现类,那么JDK启动的时候都会一次性全部加载。那么如果有的扩展点实现初始化很耗时或者如果有些实现类并没有用到,那么会很浪费资源
2.如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因

6.ServiceLoader类的分析

     ServiceLoader<DataBaseDriver> serviceLoader =ServiceLoader.load(DataBaseDriver.class);
        for(DataBaseDriver dataBaseDriver :serviceLoader){
            System.out.println(dataBaseDriver.connect("com.sunkang "));
        }

public final class ServiceLoader<S> implements Iterable<S>该ServiceLoader实现了Iterable的接口,具体了迭代器的功也就可以进行for循环了

该load的方法会会初始化LazyIterator的迭代器

 public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

那么的对应引入的驱动是如何发现并实例化的,可以猜想:
先通过扫描下 META-INF/services+接口名,如果资源存在,则读取具体的内容进行反射实例化
对应的源码这里就不解析了,的确也是这样做的。

上一篇下一篇

猜你喜欢

热点阅读