一、扩展点简介
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
约定:
在扩展类的 jar 包内 1,放置扩展点配置文件 META-INF/dubbo/接口全限定名或者META-INF/dubbo/internal/接口全限定名或者META-INF/services/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
二、扩展点源码解析
- SPI :Annotation,扩展点接口的标识,value指定缺省扩展点名。
- Adaptive:Annotation,在ExtensionLoader生成Extension的Adaptive Instance时,为ExtensionLoader提供信息。value:在URL上找key的Value作为要Adapt成的Extension名。
- Activate:Annotation,对于可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。比如,过滤扩展,有多个实现,使用Activate Annotation的扩展可以根据条件被自动加载。
- ExtensionFactory:interface,是一个扩展点,有@SPI注解,有三个实现:SpringExtensionFactory,AdaptiveExtensionFactory,SpiExtensionFactory
- AdaptiveExtensionFactory:是ExtensionFactory的扩展点Adaptive Instance。有@Adaptive注解。
- SpiExtensionFactory:会从ExtensionLoader中获取扩展点。
- SpringExtensionFactory:会从spring的ApplicationContext中获取扩展点。
- ExtensionLoader:扩展点加载器,Dubbo使用它获取扩展点。
跟代码
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("registry");
private static final Protocol refprotocol2 = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
代码1 getExtension:
1. getExtensionLoader()会new 一个ExtensionLoader,存入本地缓存。
2. getExtension()会调用createExtension(),getExtensionClasses()会加载META-INF/dubbo/接口全限定名,META-INF/dubbo/internal/接口全限定名,META-INF/services/接口全限定名的扩展点配置,如果有wrapperClasses,wrapperClasses包装原instance,生成一个新的instance。
3. cachedWrapperClasses是在loadFile()加载扩展点配置时,如果扩展点实现类有clazz.getConstructor(type)带类型的构造方法。以com.alibaba.dubbo.rpc.Protocol为例,ProtocolFilterWrapper,ProtocolListenerWrapper,QosProtocolWrapper就会包装原Protocol实例。
代码2 getAdaptiveExtension():
1. getAdaptiveExtension()就是返回有@Adaptive注解的实现类,在loadFile()加载扩展点配置时,会扫描@Adaptive注解。
2. 如果所有实现类都没有@Adaptive注解,就会调用createAdaptiveExtensionClass()动态生成一个扩展点的实现类。以com.alibaba.dubbo.rpc.Protocol为例,会生成一个名为“Protocol$Adaptive”的动态类。@SPI的value指定了默认扩展点名,@Adaptive的value指定了扩展点名从URL中的哪个属性的值,如果@Adaptive的value未指定则默认取接口名protocol。最终调用指定的扩展点名的扩展点实现类。