dubbo拓展点机制(和对JDK SPI机制的优化)

根据dubbo官网描述,dubbo自定义了一套服务发现机制,和JDK的SPI机制相比较:

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 注入其它扩展点。

具体分析,JDK的SPI机制发现服务的路径为META-INF/services/路径下的文件,文件名为接口全限定名,里面每一行为接口的实现;
JDK中加载SPI的方式为:使用类ServiceLoader.load加载路径META-INF/services/下的服务;

而在dubbo中,定义了三种加载服务的策略,分别对应不同的加载位置,都是org.apache.dubbo.common.extension.LoadingStrategy接口的实现:

● org.apache.dubbo.common.extension.DubboInternalLoadingStrategy:加载dubbo内部的服务,加载路径为:META-INF/dubbo/internal/;
● org.apache.dubbo.common.extension.DubboLoadingStrategy:加载自定义的dubbo服务,加载路径为:META-INF/dubbo/;
● org.apache.dubbo.common.extension.ServicesLoadingStrategy:使用JDK原生的加载机制,加载路径为:META-INF/services/;

dubbo在加载拓展点的时候,是遍历所有的LoadingStrategy服务实现,在loadDirectory方法中用当前策略的加载路径+要加载的接口全限定名作为资源文件路径,进行每一行内容的解析:

        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(),
                    strategy.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"),
                    strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

dubbo获取拓展点服务-示例代码

与JDK的SPI文件内容格式不同的是,dubbo的自定义服务文件中的内容为key=value的形式:例如有接口:io.itaiit.spi.ResourceLoader,和接口的实现:

  • io.itaiit.spi.impl.ResourceLoaderImpl;
  • io.itaiit.spi.impl.ResourceLoaderImpl2;

META-INF/dubbo/io.itaiit.spi.ResourceLoader文件中的内容为:

xmlloader=io.itaiit.spi.impl.ResourceLoaderImpl
xmlloader2=io.itaiit.spi.impl.ResourceLoaderImpl2

获取服务代码示例:

@Slf4j
public class AppTest {
    @Test
    void test01() {
        ExtensionLoader<ResourceLoader> extensionLoader = ExtensionLoader.getExtensionLoader(ResourceLoader.class);
        ResourceLoader loader1 = extensionLoader.getExtension("xmlloader");
        log.info(loader1.toString());
    }
}

输出为:
19:06:44.617 [main] INFO io.itaiit.AppTest - io.itaiit.spi.impl.ResourceLoaderImpl2@11dc3715

针对于三个优化的理解

  1. 类似于懒加载实例化;
    dubbo通过带泛型的ExtensionLoader类,加载并解析与泛型类型相对应的资源文件,当声明了一个ExtensionLoader对象的时候,只是加载了所有实现类的Class对象,并没有创建实例对象;当通过getExtension(String name)获得具体的实现的时候,才会创建真正的实例对象;

    并且使用与JDK SPI默认加载路径不同的路径,来存放自定义的服务发现信息,与JDK的SPI机制互不干扰;

  2. 易读的异常信息;
    加载和实例化过程中,完善了异常的提示信息:catch中抛出异常的完善,例如:

    throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                        type + ") couldn't be instantiated: " + t.getMessage(), t);
    
  3. dubbo的服务之间可以自动注入;
    dubbo提供了两种自动注入的方式:
    - byName;(默认)
    - byType;
    对于需要注入的参数,使用的注解是:org.apache.dubbo.common.extension.Inject

细节补充

  1. 服务定义文件中的服务可以设置别名:key中可以用,分隔,设置多个名字:
    xmlloader2, aliasname=io.itaiit.spi.impl.ResourceLoaderImpl2
  2. 方法ExtensionLoader.getLoadedExtension(),会从缓存中获取已经加载过的服务实例,如果没有加载过,则返回null;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值