Dubbo中的ClassLoader问题

最近看到Dubbo的SPI加载过程,对其中的动态加载Adaptive拓展点的时候获取类加载器的过程有点困惑,查阅了很多资料之后,这里记录一下

首先来看一下Dubbo中在动态编译一个拓展点的时候获取类加载器的源码:

public static ClassLoader getClassLoader(Class<?> clazz) {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
         // Cannot access thread context ClassLoader - falling back to system class loader...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = clazz.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }

        return cl;
    }

这里我们都知道Jvm默认的类加载过程是通过双亲委派机制来完成的,只有在Java的SPI机制上

因为对应的接口是在rt.jar中,是通过引导类加载器加载进来的,但是SPI对应的实现是在classpath的路径下,对引导类加载器是不可见的,所以就无法加载。

这个时候就需要用到线程上下文类加载器了,也算是变相的由上级类加载器委派下级类加载器去加载对应的实现类。

这里是有些违背Java的类加载机制,因为在Java中一个类和他引用的类应该都是由同一个类加载器加载到Jvm中的。

 

了解过上面内容之后还是不能解释这里为什么要先获取线程上下文类加载器来作为第一选择

因为我们知道在正常的Jvm环境中,线程上下文类加载器得到的就是系统类加载器(AppClassloader)

那这里为什么要先从线程上下文中先获取,如果获取不到再取当前类的类加载器,最后才去获取系统来加载器呢?(getSystemClassLoader方法获取到的就是AppClassLoader)

 

直到在GitHub上看到这个问题的时候,才有些焕然大悟:
https://github.com/apache/incubator-dubbo/issues/178

 

理由就是如果在系统环境中,有其他的类加载器,那么在通过其他类加载器可能出现无法正确加载拓展点的情况

所以这里允许用户在使用的过程中,根据实际的系统环境,来设置线程上下文类加载器,这样就可以解决系统中因为不通层级的类加载器带来的类加载问题了。

posted @ 2020-03-13 17:40  SyrupzZ  阅读(596)  评论(0)    收藏  举报