Dubbo源码分析--Dubbo扩展机制 ExtensionLoader 分析

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);
    }
}

ProxyFactory产生的AdaptiveExtension如下:

package com.alibaba.dubbo.rpc;

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


public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
    public java.lang.Object getProxy(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.getParameter("proxy", "javassist");

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

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

        return extension.getProxy(arg0);
    }

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

        com.alibaba.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");

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

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

        return extension.getInvoker(arg0, arg1, arg2);
    }
}


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

感想

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

参考

http://cxis.me/2017/02/18/Dubbo中SPI扩展机制解析/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值