作者 | 京东云开发者-京东物流 龚航林
原文链接:https://my.oschina.NET/u/4090830/blog/10116011
1 SPI 简介1.1 SPI(Service Provider Interface)
本质:将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。
JAVA SPI:用来设计给服务提供商做插件使用的。基于策略模式来实现动态加载的机制。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。
dubbo SPI:在 dubbo 中也有 SPI 机制,虽然都需要将接口全限定名配置在文件中,但是 dubbo 并没有使用 java 的 spi 机制,而是重新实现了一套功能更强的 SPI 机制,支持了 AOP 与依赖注入,并且 利用缓存提高加载实现类的性能,同时 支持实现类的灵活获取。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。例如 dubbo 当中的 protocol,LoadBalance 等都是通过 SPI 机制扩展。
2 java SPI2.1 实现过程
1)需要在 classpath 下创建一个目录,该目录命名必须是:META-INF/service
2)在该目录下创建一个 文本文件,该文件需要满足以下几个条件
3)通过 java.util.ServiceLoader 的加载机制来加载服务
2.2 工作原理
1)当调用 ServiceLoader.load (Class clz) 方法时,会到 jar 中中的目录 “META-INF/services/“ + clz.getName 进行文件读取,
2)当在调用 ServiceLoader.forEach 方法时,实际走的是 LazyIterator,当在调用 LazyIterator.hasNext 时,在文件中读取到实际的服务实现类并把它们通过调用 Class.forName (String name, boolean initialize,ClassLoader loader)。
2.3 实际应用
javaSPI 我们最熟悉的应用就是数据库驱动了,MySQL 和 oracle 驱动针对 JDBC 分别有自己的实现,这就有赖于 java 的 SPI 机制。
3 dubbo SPI3.1 实现过程
1)需要在 classpath 下创建一个目录,该目录命名可以是:META-INF/service/、META-INF/dubbo/、META-INF/dubbo/internal/
2)在该目录下创建一个 文本文件,该文件需要满足以下几个条件
3)通过 org.Apache.dubbo.common.extension.Extensier 的加载机制来加载服务
3.2 工作原理
1)我们首先通过 Extensier 的 getExtensier 方法获取一个接口的 Extensier 实例,然后再通过 Extensier 的 getExtension 方法获取拓展类对象,源码如下,首先是 getExtensier 方法:
new Extensier (type) 源码如下:
注意这里创建 Extensier 对象的构造方法如下:Extensier.getExtensier 获取 ExtensionFactory 接口的拓展类,再通过 getAdaptiveExtension 从拓展类中获取目标拓展类。
2)通过 Extensier.getExtensier 取到接口的加载器 Loader 之后,再通过 getExtension 方法获取需要拓展类对象。
以上代码首先检查 holder 中的实例缓存,缓存未命中则创建拓展对象。dubbo 中包含了大量的扩展点缓存。这个就是典型的使用空间换时间的做法。
创建拓展类对象步骤分别为:
我们接下来重点看下 getExtensionClasses 方法:
先从缓存中获取 class,缓存未命中则调用 loadExtensionClasses 方法加载,我们再看下 loadExtensionClasses 这个方法:
我们看到这里遍历调用了多个策略去加载 class 的,跟到这里我们发现非常有意思的是:dubbo 在加载 META-INF 目录下的 class 键值对的时候采用了 javaSPI 的方式
这里 dubbo 使用 javaSPI 的方式加载到 3 中类加载策略:
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy 用于加载 META-INF/dubbo/internal/ 中的 class
org.apache.dubbo.common.extension.DubboLoadingStrategy 用于加载 META-INF/dubbo/ 中的 class
org.apache.dubbo.common.extension.ServicesLoadingStrategy 用于加载 META-INF/service/ 中的 class
dubbo 的 SPI 还提供了自适应(Adaptive)、自动注入的功能就不在这里过多展开了,有兴趣可以自行了解。
3.3 实际应用
dubbo 中大量使用了 SPI 机制:
例如 dubbo 的多协议的实现:
4 javaSPI 和 dubboSPI 对比