SPI

SPI

SPI 全称为 Service Provider Interface,是一种服务发现机制。

API:给消费者调用。SPI:给服务者提供接口,用户可以自定义类实现接口。类似于留了个插件槽,用户可以自定义插件使用。

SPI:破坏了双亲委派机制。例如:利用spi加载jdbc的java.sql.Driver,保证各个sql厂家实现自己的类。SLFJ等,兼容各种日志。

Bootstrap Classloader(只拿些rt.jar下的Stirng,Math等类)加载器拿到了Application ClassLoader加载器应该加载的类,就打破了双亲委派模型。(原本先让父类进行加载,父类本来也没有,才能被子类加载,现在通过SPI,直接在Bootstrap Classloader加载到JVM)

(先让父加载器加载,然后字加载器加载。)

工作机制:创建流程

public interface Robot {
    void sayHello();
}

接下来定义两个实现类,分别为 OptimusPrime 和 Bumblebee。

public class OptimusPrime implements Robot {
    
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

public class Bumblebee implements Robot {

    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}

接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 org.apache.spi.Robot。文件内容为实现类的全限定的类名,如下:

org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee

做好所需的准备工作,接下来编写代码进行测试。

public class JavaSPITest {

    @Test
    public void sayHello() throws Exception {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);//相当于获取到所有实现接口的类
        System.out.println("Java SPI");
        serviceLoader.forEach(Robot::sayHello);
    }
}

最后来看一下测试结果,如下:

img

从测试结果可以看出,我们的两个实现类被成功的加载,并输出了相应的内容。关于 Java SPI 的演示先到这里,接下来演示 Dubbo SPI。

例子:jdbc的实现,mysql包下会配置一个文件,包含需要SPI加载的类。

DriverManager.getcollection(url,user,password);

可以看到DriverManager在初始化时会使用ServiceLoader来加载java.sql.Driver的实现类。(加载驱动不应该是Class.forName吗?)

确实JDBC驱动的加载是在Class.forName这一步完成的,但是后面的jdbc版本也支持SPI加载。(即使不写Class.forName也能加载成功)

(判断逻辑:如果Class.forName加载了数据库驱动,则不适用SPI加载,否则选择SPI进行加载。根据url的不同加载不同的驱动)

这个自动加载采用的技术叫做SPI,数据库驱动厂商也都做了更新。可以看一下jar包里面的META-INF/services目录,里面有一个java.sql.Driver的文件,文件里面包含了驱动的全路径名。

比如mysql-connector里面的内容:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

作者:tracy_668
链接:https://www.jianshu.com/p/f6653220a3e7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.2 Dubbo SPI 示例(增强javaSPI)

Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下。

optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee

与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。下面来演示 Dubbo SPI 的用法:

public class DubboSPITest {

    @Test
    public void sayHello() throws Exception {
        ExtensionLoader<Robot> extensionLoader = 
            ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");//可以获得明确的实现类
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

微信公众号也会更新哦!!

posted @ 2020-09-19 21:32  badribbit123  阅读(147)  评论(0编辑  收藏  举报