欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Jarslink源码解析-----ModuleClassLoader

程序员文章站 2022-04-10 08:28:51
ModuleClassLoader是jarslink自定义的类加载器,继承自URLClassLoader,同时可以强制指定一些包下的class,由本ClassLoader自己加载,不通过父ClassLoader加载,突破双亲委派机制。上面这句话是源码中作者为我们说明的,接下来我将借助源码及之前博客中的知识说明一下这个类主要涉及的点。我们可以从代码中看到,ModuleClassLoader继承自URLClassLoader,该类也是系统类加载器和扩展类加载器的父类(此处是父类,而不是父类加载器,这...

ModuleClassLoader是jarslink自定义的类加载器,继承自URLClassLoader,同时可以强制指定一些包下的class,由本ClassLoader自己加载,不通过父ClassLoader加载,突破双亲委派机制。

上面这句话是源码中作者为我们说明的,接下来我将借助源码及之前博客中的知识说明一下这个类主要涉及的点。

Jarslink源码解析-----ModuleClassLoader

我们可以从代码中看到,ModuleClassLoader继承自URLClassLoader,该类也是系统类加载器和扩展类加载器的父类(此处是父类,而不是父类加载器,这里是类与类之间的继承关系),URLClassLoader功能比较强大,它既可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加载类。

其实我们可以将URLClassLoader理解为ClassLoader的扩展,封装了大部分的获取类的操作,他可以从以下几种形式加载类:

  1.     文件: (从文件系统目录加载)
  2.     jar包: (从Jar包进行加载)
  3.     Http: (从远程的Http服务进行加载

并且,URLClassLoader还为Jarslink提供了一个动态加载中最重要的一个点,就是URLClassLoader实现了一个重要功能就是对ClassLoader的close,该功能是JDK 1.7中新增的功能。当class文件或者resources资源文件更新后,我们需要重新加载这些类或者Jar。从理论上来说,当应用清理了对所加载的对象的引用,那么垃圾收集器就会将这些对象给收集掉,然后我们再重新加载新的JAR文件,并创建一个新的URLClassLoader来加载。可是这里有一个问题,就是我们不知道垃圾收集器什么时候将那些未被引用的对象给收集掉,特别是在Windows中,因为在Windows中打开的文件是不可以被删除或被替换的。

jarslink目前官方版本中存在的一个问题就是每个模块卸载后的ClassLoader没有close,导致模块加载过的jar包一直占用,该情况在windows极为明显。

接下来我们来看一下jarslink是如何突破jvm的“双亲委派”的。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查是否已经加载过该类
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //如果没有加载过,调用父类加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                        //父类加载器为空,则调用启动类加载器加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 如果仍然没有加载到该类,则调用findclass方法加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

上面这段代码是ClassLoader中原有的loadClass方法,我们可以看到逻辑,若启动类加载器和父类加载器都没有加载到该类时,classLoader会调用findClass方法尝试使用当前类加载器加载,所以我们大概可以了解到若想实现我们的自定义类加载,就必须重写loadClass和findClass方法。

@Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> result = null;
        synchronized (ModuleClassLoader.class) {
            if (isEligibleForOverriding(name)) {
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("Load class for overriding: {}", name);
                }
                result = loadClassForOverriding(name, resolve);
            }
            if (result != null) {
                //链接类
                if (resolve) {
                    resolveClass(result);
                }
                return result;
            }
        }
        //使用默认类加载方式
        return super.loadClass(name, resolve);
    }

上面是ModuleClassLoader中的loadClass方法,可以看到,jarslink为了突破双亲委派机制,首先并没有尝试去按照classloader中的逻辑寻找加载,而是先通过判断该类是否要突破双亲委派,如果是的话,则调用类中的loadClassForOverriding方法

private Class<?> loadClassForOverriding(String name, boolean resolve) throws ClassNotFoundException {
        //查找已加载的类
        Class<?> result = findLoadedClass(name);
        if (result == null) {
            //加载类
            try {
                result = findClass(name);
            } catch (ClassNotFoundException e) {
                //当前子模块系统加载不到时尝试从父容器中加载
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("class will load from parent context: {}", name);
                }
                result = super.loadClass(name, resolve);
            }
        }
        return result;
    }

可以看到最终调用了findClass方法来加载类,而继承的URLClassloader已经实现了该方法,所以进需要调用即可。

本文地址:https://blog.csdn.net/bigdogLIU/article/details/112596219