Dubbo ExtensionLoader源码解读

Posted 秋风兮月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo ExtensionLoader源码解读相关的知识,希望对你有一定的参考价值。

ExtensionLoader提供了静态工厂方法public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)来获取ExtensionLoader的实例。
理解ExtensionLoader要注意区分ExtensionLoader与Extension的概念。从名字上也可以看出ExtensionLoader是Extension的加载器,可以看成是工厂类。在调用getActivateExtension、getLoadedExtension、getExtension、getDefaultExtension、getAdaptiveExtension等方法的时候,得到的是相应的extension的对象实例。

在创建ExtensionLoader实例的时候,会创建该ExtensionLoader对应Extension的工厂类ExtensionFactory。

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

其中ExtensionFactory也使用了ExtensionLoader的静态工厂方法来获取。获取ExtensionFactory的ExtensionLoader后会调用ExtensionLoader.getAdaptiveExtension方法来获取ExtensionFactory对象。

对于getAdaptiveExtension方法,首先从cachedAdaptiveInstance获取缓存的adaptive的类型为type的实例。如果不存在,会调用createAdaptiveExtension方法来创建adaptive的type类实例。

createAdaptiveExtension函数调用了getAdaptiveExtensionClass方法来获取Adaptive的Extension类。至于什么是Adaptive的Extension,稍后解析getAdaptiveExtensionClass方法的时候便可以清楚了。在获取了Extension类后,就会调用injectExtension方法来反射注入Extension实例对象的属性值。对于Extension实例中各属性值的获取,是使用objectFactory对象的工厂方法<T> T getExtension(Class<T> type, String name);来获取。
Dubbo中实现的ExtensionFactory主要有三个:

  • com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
  • com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
  • com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

下面说一下getAdaptiveExtensionClass方法的作用。

private Class<?> getAdaptiveExtensionClass() 
    getExtensionClasses();
    if (cachedAdaptiveClass != null) 
        return cachedAdaptiveClass;
    
    return cachedAdaptiveClass = createAdaptiveExtensionClass();

可以看出首先调用getExtensionClasses方法,getExtensionClasses方法的作用主要是去获取所有配置的type类型的实现类。如果cachedClasses不为空,那么直接返回cachedClasses。如果cachedClasses为空,则会调用loadExtensionClasses来加载extension类。首先会获取type上的@SPI注解。如果注解不为空,那么获取注解的value值赋值给cachedDefaultName。然后依次扫描classpath以及所依赖jar包中”META-INF/dubbo/internal/”、”META-INF/dubbo/”、”META-INF/services/”路径下配置的文件进行扫描,扫描完成后将结果赋值给cachedClasses。这个扫描是通过loadFile函数来实现的。

通过查看loadFile方法代码就可以清楚dubbo扩展配置的方法。配置文件的名称是type的全限定名。里面的内容是name=value的形式。name是名称,value是type的实现类。例如对于type为ExtensionFactory.class的配置,在dubbo的jar包中”/META-INF/dubbo/internal”下有一个名字为”com.alibaba.dubbo.common.extension.ExtensionFactory”的文件,其中内容如下:

spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactor

这三个就是对应的ExtensionFactory的实现类。

同时loadFile在扫描的过程中,会看对应的类上是否标注了@Adaptive注解。如果标注了,那么就将cachedAdaptiveClass赋值为该类。并且cachedAdaptiveClass是唯一的,不能多于一个。

对于没有标注@Adaptive注解的类,首先尝试查找类是否有以type为参数的构造函数,如果有,说明这个类是type类型的wrapper类,将该类存入cachedWrapperClasses中。如果没有该构造函数,就获取默认构造函数,从这里可以看出key可以省略。name可以从注解@Extension中获取,或者实现类的simpleName中获取,如果该类的simpleName以type的simpleName结尾,那么就会去除来得到name。然后会根据name来将该类与name的对应关系存入cachedNames中,并将类存入extensionClasses中。另外,如果类标注了@Activate注解,还会将该类存入cachedActivates中。

讲完了getExtensionClasses方法的作用,回来继续看getAdaptiveExtensionClass方法,调用完getExtensionClasses方法后,首先判断cachedAdaptiveClass是否为空,之前在getExtensionClasses方法中可以看到,如果相应的类标注了@Adaptive注解,那么cachedAdaptiveClass就不为空,这时候getAdaptiveExtensionClass直接就将cachedAdaptiveClass返回。如果cachedAdaptiveClass为空,那么会继续调用createAdaptiveExtensionClass来动态编译生成cachedAdaptiveClass。

这里具体来解析下createAdaptiveExtensionClass的作用。

private Class<?> createAdaptiveExtensionClass() 
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);

可以看出首先调用createAdaptiveExtensionClassCode来生成代码字符串,然后利用ExtensionLoader获取Adaptive的Compiler实例,Compiler的Apative类对应的是com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler。然后动态编译获取相应的类。

对于createAdaptiveExtensionClassCode方法的机制是这样的:

获取type类型接口上定义的method,然后找出这些method中有@Adaptive注解的method,然后根据该method的定义来动态生成实现方法。看代码可以看出动态创建Adaptive的类有一定的限制条件。即参数中必须要有URL类型的参数,或者某个参数的get方法返回类型是URL类型。这样才可以动态生成。因为Dubbo是所有配置信息都将转换为URL的参数。
例如com.alibaba.dubbo.rpc.Protocol接口:

@SPI("dubbo")
public interface Protocol 

    /**
     * 获取缺省端口,当用户没有配置端口时使用。
     * 
     * @return 缺省端口
     */
    int getDefaultPort();

    /**
     * 暴露远程服务:<br>
     * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
     * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
     * 
     * @param <T> 服务的类型
     * @param invoker 服务的执行体
     * @return exporter 暴露服务的引用,用于取消暴露
     * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * 引用远程服务:<br>
     * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
     * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
     * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
     * 
     * @param <T> 服务的类型
     * @param type 服务的类型
     * @param url 远程服务的URL地址
     * @return invoker 服务的本地代理
     * @throws RpcException 当连接服务提供方失败时抛出
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    /**
     * 释放协议:<br>
     * 1. 取消该协议所有已经暴露和引用的服务。<br>
     * 2. 释放协议所占用的所有资源,比如连接和端口。<br>
     * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
     */
    void destroy();

可以看出export方法和refer方法都有@Adaptive注解,refer方法中有URL类型的参数,export方法中虽然没有URL参数,但是其参数Invoker中具有getUrl方法,返回类型为URL类型。对于Protocol接口动态生成的Adaptive类代码如下:

package com.alibaba.dubbo.rpc;

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

public class Protocol$Adpative 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);
    

以上是关于Dubbo ExtensionLoader源码解读的主要内容,如果未能解决你的问题,请参考以下文章

dubbo源码梳理之ExtensionLoader

Dubbo源码分析之ExtensionLoader加载过程解析

Dubbo SPI 源码深入分析

Dubbo源码学习(四ExtensionLoader 扩展点加载机制,Protocol$Adaptive,ProxyFactory$Adaptive,Cluster$Adaptive)

Dubbo源码学习(四ExtensionLoader 扩展点加载机制,Protocol$Adaptive,ProxyFactory$Adaptive,Cluster$Adaptive)

Dubbo源码阅读笔记3