Dubbo系列<9>-JDK的SPI

   Dubbo 是基于 Java 原生 SPI 机制思想的一个改进,所以,先从 JAVA SPI 机制开始了解什么是 SPI 以后再去学习Dubbo 的 SPI,就比较容易了

什么是JDK的SPI?

        SPI 全称( service provider interface ),是 JDK 内置的一种服务提供发现机制,目前市面上有很多框架都是用它来做服务的扩展发现,大家耳熟能详的如 JDBC、日志框架都有用到;简单来说,它是一种动态替换发现的机制。举个简单的例子,如果我们定义了一个规范,需要第三方厂商去实现,那么对于我们应用方来说,只需要集成对应厂商的插件,既可以完成对应规范的实现机制。 形成一种插拔式的扩展手段

SPI 规范总结

实现 SPI,就需要按照 SPI 本身定义的规范来进行配置,SPI规范如下

1. 需要在classpath下创建一个目录,该目录命名必须是:META-INF/services

2. 在该目录下创建一个 properties 文件,该文件需要满足

以下几个条件

    a) 文件名必须是扩展的接口的全路径名称

    b) 文件内部描述的是该扩展接口的所有实现类

    c) 文件的编码格式是 UTF-8

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

真实应用场景

SPI 在 很 多 地 方 有 应 用 , 大 家 可 以 看 看 最 常 用 的java.sql.Driver 驱动。JDK 官方提供了 java.sql.Driver 这个驱动扩展点,但是你们并没有看到 JDK 中有对应的 Driver实现。 那在哪里实现呢?以连接 Mysql 为例,我们需要添加 mysql-connector-java依赖。然后,你们可以在这个jar包中找到SPI的配置信息。如下图,所以 java.sql.Driver 由各个数据库厂商自行实现。这就是 SPI 的实际应用。当然除了这个意外,大家在 spring的包中也可以看到相应的痕迹


按照同样思路可实现啊:

比如说我们连接数据,DataSource定义了规范,然后Mysql和oracle自己实现规范,最后我们要选哪一最后作为我们的规范,完全是由咱们自己来决定。

如下图所示:


JDK  SPI缺点:

1. JDK标准的SPI会一次性加载实例化扩展点的所有实现,什么意思呢?就是如果你在 META-INF/service 下的文件里面加了 N 个实现类,那么 JDK 启动的时候都会一次性全部加载。那么如果有的扩展点实现初始化很耗时或者如果做技术人的之路明灯,做职场生涯的精神导师有些实现类并没有用到,那么会很浪费资源

2. 如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因

新建项目spi-demo,同时创建module:api,mysql,oracle,demo


api


package com.tian;

public interface DataSource {
    String connect(String msg);
}

mysql


package com.tian.mysql;

import com.tian.DataSource;

public class MysqlDataSource implements DataSource {
@Override
    public String connect(String msg) {
        System.out.println("mysql");
        return null;
    }
}


oracle


package com.tian.oracle;

import com.tian.DataSource;

public class OraclelDataSource implements DataSource {
@Override
    public String connect(String msg) {
        System.out.println("oracle");
        return null;
    }
}


demo


这里是把两种都加载进来了,

package com.tian;

import java.util.ServiceLoader;

public class DataSourceFactory {
public DataSourceFactory() {
    }

public static void getDataSource() {
        ServiceLoader<DataSource> serviceLoader = ServiceLoader.load(DataSource.class);
        for (DataSource dataSource : serviceLoader) {
            dataSource.connect("1111");
        }
    }
}


package com.tian;

public class DemoMain {
public static void main(String[] args) {
      DataSourceFactory.getDataSource();
    }
}


运行Demo输出


如果我们只在demo中pom.xml中导入mysql.jar。

再次运行Demo。那么最后输出:


自此咱们已经实现了JDK的SPI。后面Dubbo源码里面大量使用SPI机制(Dubbo中是JDK-SPI的升级版本)


猜你喜欢

转载自blog.51cto.com/10983206/2564048