Dubbo 源码解读——自定义 Classloader 之 ExtensionLoader

    众所周知,Dubbo 是阿里巴巴公司自主研发开源的一个高性能的服务框架(现已捐献给 Apache 基金会组织),应用之间可以通过 RPC 的方式来互相调用并返回结果。主要基于 Java 语言开发,它提供了三大核心能力:

        1. 面向接口的远程方法调用;

        2. 智能容错和负载均衡;

        3. 以及服务自动注册和发现;

   (图来自 dubbo 官网)

 对于 Dubbo 的特性,我这里不做过多的介绍。接下来,进入正题。

1. 首先,下载源码,源代码地址为:

git clone https://github.com/apache/incubator-dubbo.git

,如果网速不好的话,耗费时间会稍长一些,请耐心等待。( 本人版本为:2.7.0-SNAPSHOT)

 

2. 下载成功后,用 IDEA 打开,如下图:

eb289540a2ec5182258a18dd3e5a2ea128d.jpg

3. 下面我们进入正题, 直接看  dubbo-common 模块中的 org.apache.dubbo.common.extension 包,也就是本文重点解读的自定义 ClassLoader 位置。Dubbo 扩展点主要都是从 ExtensionFactory 接口进行的,我们从这个接口类开始进行分析。

0ce8fd834411b3e99ffcabe061e382297a6.jpg

4.  ExtensionFactory 接口很简单,里面只有一个方法  getExtension, 而这个 接口一共有三个实现类。分别为:SpiExtensionFactory (SPI 的方式类动态加载)、AdaptiveExtensionFactory(Adaptive 的方式类动态加载)、SpringExtensionFactory(Spring 的方式类动态加载), 这里我们重点以 SpiExtensionFactory 的方式进行分析。

/**
 * ExtensionFactory
 */
@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

8ef524c54e630aa2b3906f822c3e2ff0b4b.jpg

5.  进行  SpiExtensionFactory 类中,只有一个 ExtensionFactory 的实现方法。首先,判断该 type 是否为接口类型,并且具有 SPI  注解的标识,则调用 自定义扩张类加载器,对 type 类进行动态加载,如果加载成功,则返回该  type 自适应的类的对象实例。

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

6. 接下来,我们进入  ExtensionLoader.getExtensionLoader(type); 进行分析。对 type 进行类型检查判断,如果不符合要求抛出异常,最后由 EXTENSION_LOADERS.get(type) 获取该类的扩展的 ClassLoader 。

    EXTENSION_LOADERS  为  ConcurrentMap, 其中 Key 为  Class<?> 类型,Value 为  ExtensionLoader<?> 类型。也就是说,对应的类有自己的自定义类加载器。

    在这里获取的时候为 Null , 接下来 new ExtensionLoader 对象,构造方法中,传入 type ,并回放到 ConcurrentMap 中。

        最终,返回  type 对应的自定义  ExtensionLoader 对象。

@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)
        throw new IllegalArgumentException("Extension type == null");
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }

    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

 

7. 进入到  new ExtensionLoader<T>(type)  中,继续阅读源码。 这里需要注意,该类的构造方法为私有! type 赋值给了全局变量 type 。

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

 

8.  接下来是  ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()) 方法。

这里是连着两个调用, getExtensionLoader 我们在第 6 步的时候已经阅读过,返回的对象是参数 type 的类加载器。这里返回的是 ExtensionFactory 类加载器,进入到  getAdaptiveExtension() 方法继续阅读。

 

9.  从 cachedAdaptiveInstance 获取实例, cachedAdaptiveInstance 是一个泛型类,其中只有两个方法  get 、set 两个方法。 还有一个被  volatile 表示的属性 value 。主要是为了将动态加载类对象  instance 临时缓存。

    接下来,对 createAdaptiveInstanceError 进行判断。如果不为空,直接抛出 IllegalStateException 信息。

   再往下走,对 cachedAdaptiveInstance 进行加锁,再进行一次 非空判断(你应该能够想到了——单例模式),结合上面的  private 构造函数、volatile 和 双重检查判断,可以说种种迹象都是为了避免并发造成多个对象创建出现问题。

      下面进入真正的创建实例方法 createAdaptiveExtension();

@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}

10.  在该方法中,首先是获取 class ,最后利用反射成该对象。

@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

 

11.  调用  getExtensionClasses() ,加载可扩展类。接着判断  cachedAdaptiveClass 是否为空,如果为空,说明该  cachedAdaptive 类还么有初始化,则对其进行初始化。之后,返回该 class 对象。

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

 

12.  从缓存 Holder 里面获取该类 class ,第一次初始化时为空,对其加锁,同步的方式进行加载;

    并对其进行双重检查;执行 loadExtensionClasses() 加载改 class ,  并将该 class 类型存入到 cachedClasses 中。这样做有一个好处就是,只需要初始化一次,后期如果需要加载,直接从内存中获取即可。 

private Map<String, Class<?>> getExtensionClasses() {
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

 

13.  由于在在上一步的时候,我们有看到 synchronized ,所以,这里的 反射加载为 同步的方式。

    获取该扩展类上的 SPI 注解信息(关于 SPI 注解,该类比较简单,只有 value 属性,读者可以自行查看),获取其 value 值,以该值作为 cacheName  的 Key。如果 value 值为多个值,以 “,” 分割,取第一个为 cacheName 。

    紧接着,分别加载  classpath  下  【META-INF/services/ 】、【META-INF/services/internal/

】和 【META-INF/dubbo/ 】文件夹中的 SPI 配置。

    type.name 将 com.alibaba 替换为  org.apache 是为了确保,需要加载的类包老版本兼容问题。统一成  org.apache 版本。

// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    return extensionClasses;
}

 

14.    获取 ExtensionLoader 类的  classloader, 如果获取到,使用该 classloader 加载 type 类;未获取到,则使用 java.lang.ClassLoader  从所有的搜索路径中查找 type 名称的资源。正常获取到了,则进行加载 资源。

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
    String fileName = dir + type;
    try {
        Enumeration<java.net.URL> urls;
        ClassLoader classLoader = findClassLoader();
        if (classLoader != null) {
            urls = classLoader.getResources(fileName);
        } else {
            urls = ClassLoader.getSystemResources(fileName);
        }
        if (urls != null) {
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception when load extension class(interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}

15.  使用流的方式加载资源(也就是上面提到的  META-INF 目录下的文件),按行读取,并进行加载。这里有一个小细节,需要说明一下,按行读取后,使用了 “=” 分隔符,前面为 name 后面为需要加载的class 类。(为了节省篇幅,这里略去部分代码)        

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        ......
        BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
        ......
            while ((line = reader.readLine()) != null) {
        ......
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();

                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
        ......
                      }
                }
            }
        ......


}

        也就是为什么我们能够看到在类似下图文件中所看到的内容(JDK 自带的 SPI 方式,是不能解析  xx=xxx.xxx ),dubbo 通过扩展的方式,进行了支持,同时这个等号左边的 name, 也就是我们在动态配置时,需要指定的名称。可以说,具有一举两得的功效。

053648f85eee615fa6dd1fa8f22e3e64c79.jpg

16. 到 loadClass 方式中,开始了真正的类加载工作。

    这个方法主要是对 clazz 进行加载,并对该动态类上的不同类型分别做不同的初始化工作,将加载后的 clazz 对象放入到  cachedNames 、cachedActivates、cachedAdaptiveClass 或 cachedWrapperClasses 中,进行了缓存。方便下一次获取时不用再次加载。

    源码,读者可自行查看。

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error when load extension class(interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + "is not subtype of interface.");
    }
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        } else if (!cachedAdaptiveClass.equals(clazz)) {
            throw new IllegalStateException("More than 1 adaptive class found: "
                    + cachedAdaptiveClass.getClass().getName()
                    + ", " + clazz.getClass().getName());
        }
    } else if (isWrapperClass(clazz)) {
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    } else {
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
            }
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            for (String n : names) {
                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                Class<?> c = extensionClasses.get(n);
                if (c == null) {
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                }
            }
        }
    }
}

 

以上内容为 Dubbo 动态加载扩展类的机制。

================================================

由于本文作者水平有限,难免有些内容分析错误。

感谢你的理解与反馈!

 

转载于:https://my.oschina.net/Rayn/blog/1856643

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值