【SPI】 --- java spi 机制简介

本文对应源码地址:https://github.com/nieandsun/dubbo-study


文章目录

  • 1 spi需要解决的问题剖析
  • 2 java spi使用简介
    • 2.1 使用java spi需要遵循的约定
    • 2.2 示例

1 spi需要解决的问题剖析

首选看如下代码:

@Test
public void demo1() {
    JdbcService jdbcService = new JdbcServiceAImpl();
    int i = jdbcService.insert("james");
    if (i == 1) {
        System.out.println("插入成功");
    }
}

相信上面的代码对于只要有过java开发经验的人员来说,肯定都非常熟悉。

但是设想这样一个场景,假设如果此时只有JdbcService接口,没有其具体实现,那我们能不能就非要拿到一个实现类(或者说一个符号),将上面的程序完成,等到你真有实现类了,我就直接去某个地方找到你的实现类,来调用你的方法呢?

其实这就是SPI所要解决的问题。


SPI 全称 Service Provider Interface, 是 Java 提供的一套用来被第三方实现或
者扩展的 API, 它可以用来启用框架扩展和替换组件。

【SPI】 --- java spi 机制简介_第1张图片
可以看到, SPI 的本质, 其实是帮助程序, 为某个特定的接口寻找它的实现类。 而且哪些实现类的会加载, 是个动态过程(不是提前预定好的)。

有点类似 IOC 的思想, 就是将装配的控制权移到程序之外, 在模块化设计中这个机制尤其重要。 所以 SPI 的核心思想就是解耦。

比较常见的例子:

  • 数据库驱动加载接口实现类的加载
    JDBC 加载不同类型数据库的驱动
  • 日 志门面接口实现类加载
    SLF4J 加载不同提供商的日 志实现类
  • Spring
    Spring 中大量使用了 SPI, 比如: 对 servlet3. 0 规范对
    ServletContainerInitializer 的实现、 自 动类型转换 Type Conversion
    SPI(Converter SPI、 Formatter SPI) 等

2 java spi使用简介


2.1 使用java spi需要遵循的约定

要使用 Java SPI, 需要遵循如下约定:

  • (1) 当服务提供者提供了接口的一种具体实现后, 在 jar 包的META-INF/services目 录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
  • (2) 接口实现类所在的 jar 包放在主程序的 classpath 中;
  • (3) 主程序通过 java. util. ServiceLoder 动态装载实现模块, 它通过扫描META-INF/services 目 录下的配置文件找到实现类的全限定名, 把类加载到 JVM;
  • (4)SPI 的实现类必须携带一个不带参数的构造方法;

2.2 示例

(1)先定义一个接口

public interface JdbcService {
    int insert(String name);
}

(2)再定义一系列它的具体实现

  • 第一个实现
public class JdbcServiceAImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,调通了A实现!");
        return 1;
    }
}
  • 第二个实现
public class JdbcServiceBImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,调通了B实现!");
        return 1;
    }
}
  • 第三个实现
public class JdbcServiceCImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,调通了C实现!");
        return 1;
    }
}

等等


(3) 在 源码的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
【SPI】 --- java spi 机制简介_第2张图片

  • 测试类
/**
 * java spi机制验证
 */
@Test
public void javaSPI() {
    //服务加载器,加载实现类
    ServiceLoader<JdbcService> serviceLoader = ServiceLoader.load(JdbcService.class);

    //serviceLoader是实现了Iterable的迭代器,直接遍历实现类
    for (JdbcService service : serviceLoader) {
        int james = service.insert("james");//依次调用实现类
        System.out.println(james);
    }
}

可以看到本测试类与本文刚开始时的代码最明显的不同就是,这里并没有出现任何接口的具体实现类。

也就是说ServiceLoader可以根据下面三个信息,生成具体的接口实现类。

  • (1)接口的类型 — 这里是JdbcService.class
  • (2)在META-INF/services下以接口的全限定名为名称的文件
  • (3)以及文件里的实现类的全限定名

简单看一下测试结果:
【SPI】 --- java spi 机制简介_第3张图片


这篇文章让我想起了《【springboot】— springboot的starter原理探究 + 如何自定义自己的starter》!!!


end!!!

你可能感兴趣的:(dubbo知识点整理,Spring)