由于最近在看dubbo,对里面提到的SPI一直不懂是什么意思,因为之前自己在网上搜索的时候,直接搜索的是SPI,导致看到的文章和我心里想到不一样,昨天一想我应该搜索Dubbo SPI这才揭开了我想要学习的SPI的神秘的面纱。废话说完了,来看一下到底什么是SPI吧!当然此博客的内容是在看了本文提到的参考博客的前提下的理解,说的不对的地方还请各位大佬不吝赐教!
SPI全称为Service Provider Interface,原本是Java中JDK内置的一种服务提供发现机制,目的是让不同的厂商真对统一接口编写不同的实现,进而动态的查找服务,实现方式是将接口的实现类放在指定目录的配置文件中,在程序运行过程中读取配置文件,通过反射加载实现类,在运行时就可以动态的替换接口的实现类。Java的JDBC就是这一概念的落地,以及Dubbo在JDK的SPI机制的基础之上的优化的SPI。
说白了,SPI就是“接口+策略模式+配置文件”的动态加载服务实现的机制。
了解Dubbo的SPI之前需要先了解JDK的SPI,它提供了一种在不同模块之间基于接口编程,模块之间不对接口的实现进行硬编码,依旧可以为某个接口寻找服务的实现的机制,在java.util.ServiceLoader
类中很好的说明的SPI机制。
举个,JDBC使用的java.sql.Driver接口,由于数据库的种类很多,使用的底层协议也很多,为了方便使用,所以约定一个接口,各大厂商进行各自的实现,但实现完之后,如何知道用哪个接口和哪个实现类呢,所以就有了SPI机制,将实现类写在制定目录的配置文件中。
java.util.ServiceLoader
有说:
A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file’s name is the fully-qualified binary name of the service’s type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line
也就是在resources
目录下的META-INF/services
下(没有则手动创建目录),将实现类的全限定名写在名为接口的全限定名的文件中。当引用了mysql-connector-java的jar时,就会找这个jar包下的META-INF/services
目录下的接口的全限定名的文件,读取文件中的实现类,将实现类进行加载和初始化等操作就可以使用了。
这里写个来实现一下java 的SPI:
我这里就把所有的class内容贴出来了,代码就不粘了,都比较简单,可以自己实现一下。源码分析我这里就不粘了,可自行debug进行分析,也可以参考此博客的源码分析
为啥Dubbo要在JDK的SPI基础上进行优化加强而不是直接用呢,分析了源码就知道了,最主要的原因就是java SPI是将配置文件里的实现类全部实例化,不管你用到还是没有用到,这不就做了无用功了嘛,浪费资源。
那下面来说说Dubbo的SPI扩展点机制
在Dubbo的官方文档说明中,Dubbo的整体设计分为10层,如下图:
官方说明为:图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
可见,Dubbo 就依靠 SPI 机制实现了很多的扩展功能,实现了面向功能进行拆分的对扩展开放的架构。
那么在JDK的SPI机制的基础上,Dubbo做了哪些变化呢?继续看官方文档
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
1、JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
2、如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
3、增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
总结一下Dubbo SPI的优化的地方:实现类按需加载,自适应扩展机制,增加IOC和AOP的支持。
Dubbo 对配置文件目录的约定,不同于 Java SPI ,Dubbo 分为了三类目录:
META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件。
META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件。
Dubbo的实现是通过ExtensionLoader类似java 的ServiceLoader,具体源码的实现我这里就不分析了,以后仔细分析后之后再更,源码分析可以看官方文档或大佬博客copy了一下流程,可以自行查看大佬博客对源码的分析。
参考链接
1、Dubbo中的SPI机制
2、dubbo之SPI解析
3、阿里面试真题:Dubbo的SPI机制
--------------------------你知道的越多,不知道的越多-------------------------