Java SPI 与 Dubbo SPI

starlin 1,156 2020-08-01

最近在看dubbo的源码,其中有了解到Java的SPI机制,特此记录下(更详细的介绍还请看官方文档)
同时也会记录下Dubbo SPI的实现方式

SPI初探

SPI的全称是Servcie Provider Interface,起初是提供给厂商做插件开发的

Java SPI使用了策略模式,一个接口多种实现。我们只声明接口,具体的实现并不在程序中直接确定,而是由程序之外的的配置掌控,用于具体实现的装配

和java spi 比较,dubbo SPI 做了一定的改进和优化,官方文档中描述如下:

  1. JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现则初始化很耗时,如果没有用上也加载,则浪费资源
  2. 如果扩展加载失败,则连扩展的名称都获取不到
  3. 增加了对扩展IOC和AOP的支持,一个扩展可以直接setter注入其他扩展。Dubbo SPI只加载配置文件中的类,并分成不同的种类缓存在内存中,而不会立即全部初始化

另外Dubbo SPI配置文件的用途,将其分成了三类目录:

  • META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI
  • META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件
  • META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件

然后,Dubbo 将 SPI 配置文件改成了 KV 格式,例如:

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

Java SPI实现步骤

  1. 定义一个接口以及对应的方法
  2. 编写该接口的一个实现类
  3. 在META-INF/services/目录下,创建一个以接口全路径命名的文件,文件内容为具体实现类的全路径名,如果有多个,用分行符号隔开
  4. 在代码中通过java.util.ServiceLoader来加载具体的实现类

java spi demo演示

  1. 定义1个service
public interface PrintService {
    void sayHello();
}
  1. 定义实现类
public class PrintServiceImpl implements PrintService{
    @Override
    public void sayHello() {
        System.out.println("hello java spi");
    }
}
  1. resources中增加文件添加META-INF.service文件夹,并且在其中定义一个以接口全名为文件名称的文件,我的文件名称如下:
com.alibaba.dubbo.registry.spi.PrintService
  1. 文件内容是实现类的全路径,我的文件内容如下
com.alibaba.dubbo.registry.spi.PrintServiceImpl
  1. 测试方法:
public class TestSpi {
    public static void main(String[] args) {
        ServiceLoader<PrintService> services = ServiceLoader.load(PrintService.class);
        for (PrintService printService : services) {
            printService.sayHello();
        }
    }
}

不出意外,输出结果为:

hello java spi

dubbo spi demo

  1. 在resources目录下的META-INF中建立配置文件目录dubbo/internal
    名称为:com.alibaba.dubbo.registry.spi.PrintService
    内容为:impl=com.alibaba.dubbo.registry.spi.PrintServiceImpl

  2. 在原来service上加上注解@SPI("impl")

@SPI("impl")
public interface PrintService {
    void sayHello();
}
  1. 实现类保持不变
public class PrintServiceImpl implements PrintService{
    @Override
    public void sayHello() {
        System.out.println("hello java spi");
    }
}
  1. 测试方法:
public class TestDubboSpi {
    public static void main(String[] args) {
        PrintService printService = ExtensionLoader.getExtensionLoader(PrintService.class).getDefaultExtension();
        printService.sayHello();
    }
}

输出结果:

hello java spi

以上,感谢阅读,end!!!


# Dubbo