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)
方法获取条件相匹配的扩展实现。