美文网首页
Dubbo扩展机制 ExtensionLoader 分析

Dubbo扩展机制 ExtensionLoader 分析

作者: 三云_16d2 | 来源:发表于2018-09-28 17:55 被阅读0次

ExtensionLoader是dubbo SPI的核心,暴露的两个核心方法如下:

 Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
 Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");

首先分析下getExtensionLoader(Protocol.class).getAdaptiveExtension() 方法内部逻辑,主要分为两部分:

一. ExtensionLoader.getExtensionLoader(Class type)

此方法是一个静态方法,ExtensionLoader 内部定义了一个map,维护Class<---> ExtensionLoader 之间的对应关系,最开始Protocol.class这个类找不到的时候会new ExtensionLoader()并放入到map中。

ExtensionLoader 构造函数内部定义如下:

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

对于非ExtensionFactory会再次调用ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取到ExtensionFactory的具体实现作为扩展点工厂类,这个由dubbo内部维护,会返回AdaptiveExtensionFactory。

二. ExtensionLoader.getAdaptiveExtension()

在前面一步得到ExtensionLoader实例之后调用getAdaptiveExtension(),这个方法内部主要分为两个步骤:

  1. getAdaptiveExtensionClass().newInstance(); // 得到 AdaptiveExtensionClass并实例化
  2. injectExtension(extensionInstance); // 为 extensionInstance 注入属性

1. getAdaptiveExtensionClass() 内部主要下面两个步骤

  1. getExtensionClasses(); // 负责从jar包中加载SPI 配置信息
  2. cachedAdaptiveClass = createAdaptiveExtensionClass(); //动态创建AdaptiveExtensionClass 源代码并编译成Class

getExtensionClasses()

getExtensionClasses() 调用关系如下

1. ExtensionLoader.getExtensionClasses();
    1) this.cachedClasses = loadExtensionClasses();
    2) this.cachedClasses;

2. ExtensionLoader.loadExtensionClasses()
    1) Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    2) loadFile(extensionClasses,"META-INF/dubbo/internal/");   //!/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
    3) loadFile(extensionClasses,"META-INF/dubbo/");            //!/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
    4) loadFile(extensionClasses, "META-INF/services/");        //!META-INF/services/com.alibaba.dubbo.rpc.Filter
    5) return extensionClasses;

Dubbo可以尝试中三个路径中loadClass, dubbo内置 的META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件内容如下:

    registry=class com.alibaba.dubbo.registry.integration.RegistryProtocol
    injvm=class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
    thrift=class com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
    dubbo=class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    mock=class com.alibaba.dubbo.rpc.support.MockProtocol
    http=class com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
    redis=class com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
    rmi=class com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol

dubbo 直接通过Class.forName()的形式加载这些SPI的扩展类,并且缓存在cachedClasses,注意这是所有的ExtensionClass,和AdaptiveExtension 存在区别。

createAdaptiveExtensionClass()

createAdaptiveExtensionClass 方法内部逻辑如下

    1) String code = createAdaptiveExtensionClassCode();        // 通过interface className 动态创建Source Code
    2) ClassLoader classLoader = findClassLoader();
    3) com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    4) return compiler.compile(code, classLoader);

createAdaptiveExtensionClassCode 通过SPI 接口动态产生Code,Protocol产生的AdaptiveExtension如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;


public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException(
            "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException(
            "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public com.alibaba.dubbo.rpc.Exporter export( com.alibaba.dubbo.rpc.Invoker arg0)
        throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) {
            throw new IllegalArgumentException(
                "com.alibaba.dubbo.rpc.Invoker argument == null");
        }

        if (arg0.getUrl() == null) {
            throw new IllegalArgumentException(
                "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        }

        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = ((url.getProtocol() == null) ? "dubbo"
                                                      : url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
                url.toString() + ") use keys([protocol])");
        }

        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
                                                                                                   .getExtension(extName);

        return extension.export(arg0);
    }

    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
        com.alibaba.dubbo.common.URL arg1)
        throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) {
            throw new IllegalArgumentException("url == null");
        }

        com.alibaba.dubbo.common.URL url = arg1;
        String extName = ((url.getProtocol() == null) ? "dubbo"
                                                      : url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
                url.toString() + ") use keys([protocol])");
        }

        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
                                                                                                   .getExtension(extName);

        return extension.refer(arg0, arg1);
    }
}

可以看到内部最核心的一句:
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName);
通过动态getExtension(extName)来load具体的spi实现。还记得前面的loadExtensionClasses定义的那个map吗?getExtension实际上就是从这个map中get具体的extension。

感想

看完所有代码 dubbo 无非希望动态通过name得到Extension,采用这么绕的方案,确实不太高明啊。

相关文章

网友评论

      本文标题:Dubbo扩展机制 ExtensionLoader 分析

      本文链接:https://www.haomeiwen.com/subject/zmipoftx.html