Java的SPI机制
SPI(service provider interface)机制是通过定义服务接口标准,让不同的厂商去实现,java通过java.util.ServiceLoader类查找接口对应的服务实现。
SPI约定
服务提供者提供了服务接口的一种实现后,要在类路径下的META-INF/services/目录里创建一个以服务接口命名的文件,文件内容是实现该服务接口的具体实现类名(如果有多个实现类则换行保存,SPI会一次性实例化所有实现)。
使用:ServiceLoader<XX> loader = ServiceLoader.load(XX.class);
dubbo的扩展机制
dubbo的扩展机制和java的SPI机制相似,但是又增加了如下功能:
- 根据关键字获取特定的扩展实现,依赖注解
@SPI和@Adaptive。 - 对扩展点增加了 IOC 和 AOP 的功能。
约定:在扩展类的 jar 包内 ,放置扩展点配置文件
META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
通过 ExtensionLoader 获取扩展点实例的方法:eg Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();。

核心步骤:
- 5
loadFile()从META-INF/dubbo/、META-INF/dubbo/internal、META-INF/services等路径下加载所有的扩展点 - 7 生成扩展点自适应类的源码
- 9 注入依赖的扩展点
扩展点定义
@SPI注解:被该注解标记的接口表示是一个可扩展的接口,注解的值表示默认的扩展点实现类对应的key(具体是在根据URL配置动态获取实现类时,如果URL配置缺失则使用@SPI的值作为默认值)。
@Adaptive注解:
- 标记在扩展接口的方法上,
ExtensionLoader.getAdaptiveExtension()获取自适应类是通过生成java源码并编译成class加载完成的,没有被标注的方法生成的实现都会抛出UnsupportedOperationException异常。 - 标记在扩展接口的实现类上,获取自适应类不再通过生成java源码实现,而是直接把标记了
@Adaptive注解的类作为扩展接口实现类。(只能有一个实现类标记@Adaptive,目前只有AdaptiveCompiler和AdaptiveExtensionFactory类上标注了该注解)
扩展点特性
扩展点自适应(Adaptive)
上述通过ExtensionLoader的getAdaptiveExtension()方法得到的是扩展点的自适应实现(命名方式为”类名$Adaptive”),这个自适应实现类在运行时根据url(Dubbo使用URL对象传递配置信息)来动态的创建具体实现类实例,然后再进行调用。
运行时创建的具体扩展实现类实例是通过ExtensionLoader.getExtensionLoader(XX.class).getExtension(extName);方法完成的。
生成自适应类并编译
自适应类是通过动态拼接而成的java代码,然后再通过编译器扩展(默认是javassist)编译该java代码。
|
|
Protocol自适应类源码
|
|
扩展点装饰(AOP)
ExtensionLoader在加载扩展点时,如果加载到的扩展点有拷贝构造函数(也就是构造函数参数类型为扩展点类型),则判定为扩展点的 Wrapper 类(判定扩展点是否为 Wrapper 类是在loadFile()中完成的)。
在上述介绍中,dubbo在运行时通过URL来动态创建扩展点实现的实例,也就是ExtensionLoader的getExtension(String name)方法,真正创建实例是在createExtension()方法中并完成了Wrapper类的包装过程。
|
|
扩展点注入(IOC)
在获取扩展点自适应类的实例(getAdaptiveExtension())和获取扩展点具体实现类的实例时(getExtension())都会通过injectExtension()完成依赖注入。
dubbo中默认采用AdaptiveExtensionFactory来获取所有的扩展,其实现还是通过遍历SpiExtensionFactory(dubbo的扩展机制得到的扩展点实例)和SpringExtensionFactory(spring容器维护的bean)完成的。
扩展点自动激活(Activate)
对于集合类扩展点,比如: Filter , InvokerListener , ExportListener , TelnetHandler , StatusChecker 等,可以同时加载多个实现,可以用自动激活根据条件来自动加载。
ExtensionLoader在通过loadFile()加载扩展配置时会缓存带有注解@Activate的类的相关信息,使用getActivateExtension(URL url, String[] values, String group)方法获取条件相匹配的扩展实现。