自定义类加载器的父类为何是AppClassLoader说明

 更新时间:2022年11月21日 09:26:08   作者:小小少年_  
这篇文章主要介绍了自定义类加载器的父类为何是AppClassLoader说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

首先有一个概念要了解

我们通常说在jdk中,默认有三个类加载器,bootstrapClassLoader、ExtClassLoader、AppClassLoader,我们说前者是后者的父类加载器,但是实际上,这里所谓的父类加载器,并不是Java中的父子类继承关系,而是说:

  • AppClassLoader中有一个parentClassLoader设置的值是ExtClassLoader
  • ExtClassLoader中的parentClassLoader设置的是bootstrapClassLoader
  • 我们自定义的类加载器,对应的parentClassLoader是AppClassLoader

我们如果自定义一个类加载器,默认设置的父类加载器是AppClassLoader,这个原因是在因为在初始化自定义类加载器的时候,会指定其parentClassLoader为AppClassLoader

Launcher

这个类是jre中用来启动main()方法的入口,在这个类中,我们着重关注的是初始化构造方法

public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        // 初始化extClassLoader
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        // 初始化appClassLoader,这里的这个赋值是比较重要的
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }

        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }

        System.setSecurityManager(var3);
    }
}

这个构造方法中,我目前所了解到的,就是初始化了AppClassLoader和ExtClassLoader,并且,和这篇博客相关的,我们只需要关心this.loader这个全局变量,这个全局变量存放的是AppClassLoader的对象信息

构造一个自定义类加载器

我们要自定义一个类加载器,只需要继承classLoader,并重写findClass()方法即可

class MyClassLoader extends ClassLoader {

    private String loadPath;

    public String getLoadPath() {
        return loadPath;
    }

    public void setLoadPath(String loadPath) {
        this.loadPath = loadPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = loadByte(name);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return null;
    }

    private byte[] loadByte(String className) throws Exception {
        className = className.replaceAll("\\.", "/");
        FileInputStream fileInputStream = new FileInputStream(loadPath + "/" + className + ".class");
        int available = fileInputStream.available();
        byte[] data = new byte[available];
        fileInputStream.read(data);
        fileInputStream.close();
        return data;
    }
}

那么,我在使用的时候,只需要调用new MyClassLoaderTest()即可

new MyClassLoaderTest()

我们说,自定义的类解析器对应的parentClassLoader,就是在空参构造函数中被赋值的

因为MyClassLoaderTest继承了ClassLoader,所以,会调用到ClassLoader的空参构造函数

protected ClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
    if (ParallelLoaders.isRegistered(this.getClass())) {
        parallelLockMap = new ConcurrentHashMap<>();
        package2certs = new ConcurrentHashMap<>();
        domains =
            Collections.synchronizedSet(new HashSet<ProtectionDomain>());
        assertionLock = new Object();
    } else {
        // no finer-grained lock; lock on the classloader instance
        parallelLockMap = null;
        package2certs = new Hashtable<>();
        domains = new HashSet<>();
        assertionLock = this;
    }
}

我们会发现,parentClassLoader就是getSystemClassLoader()返回的,

java.lang.ClassLoader#getSystemClassLoader
    java.lang.ClassLoader#initSystemClassLoader


// 这个方法中的其他变量我们可以暂时先不关心,我们看到有获取到一个Launcher对象
private static synchronized void initSystemClassLoader() {
    if (!sclSet) {
        if (scl != null)
            throw new IllegalStateException("recursive invocation");
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
        if (l != null) {
            Throwable oops = null;
            // 在这里调用其getClassLoader()方法,将返回的值,赋值给scl,而这个scl就是入参中的parent
            scl = l.getClassLoader();
            try {
                scl = AccessController.doPrivileged(
                    new SystemClassLoaderAction(scl));
            } catch (PrivilegedActionException pae) {
                oops = pae.getCause();
                if (oops instanceof InvocationTargetException) {
                    oops = oops.getCause();
                }
            }
            if (oops != null) {
                if (oops instanceof Error) {
                    throw (Error) oops;
                } else {
                    // wrap the exception
                    throw new Error(oops);
                }
            }
        }
        sclSet = true;
    }
}

这里的getClassLoader()返回的就是上面我说明的,需要特别关注的this.classLoader这个全局变量

scl是从l.getClassLoader()这个方法获取到的结果,那我们看下这个方法

sun.misc.Launcher#getClassLoader
public ClassLoader getClassLoader() {
        return this.loader;
}

可以看到,这里的getClassLoader,就是博客上面 Launcher 这个方法中赋值的this.loader;

所以,通过上面的代码,可以发现,我们自定义的类解析器,是在初始化的时候,指定了parent为AppClassLoader

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 完美解决MybatisPlus插件分页查询不起作用总是查询全部数据问题

    完美解决MybatisPlus插件分页查询不起作用总是查询全部数据问题

    这篇文章主要介绍了解决MybatisPlus插件分页查询不起作用总是查询全部数据问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 手动模拟JDK动态代理的方法

    手动模拟JDK动态代理的方法

    这篇文章主要介绍了手动模拟JDK动态代理的方法,帮助大家更好的了解和学习Java 代理的相关知识,感兴趣的朋友可以了解下
    2020-11-11
  • 实例解析Java设计模式编程中的适配器模式使用

    实例解析Java设计模式编程中的适配器模式使用

    本篇文章主要通过实例对适配器模式进行了详解,需要的朋友可以参考下
    2017-04-04
  • Java基于代理模式解决红酒经销问题详解

    Java基于代理模式解决红酒经销问题详解

    这篇文章主要介绍了Java基于代理模式解决红酒经销问题,详细描述了代理模式的概念、原理并结合实例形式分析了java基于代理模式解决红酒经销问题的相关步骤、实现方法与操作注意事项,需要的朋友可以参考下
    2018-04-04
  • 关于@Scheduled参数及cron表达式解释

    关于@Scheduled参数及cron表达式解释

    这篇文章主要介绍了关于@Scheduled参数及cron表达式解释,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Docker 快速部署Springboot项目超详细最新版

    Docker 快速部署Springboot项目超详细最新版

    这篇文章主要介绍了Docker 快速部署Springboot项目超详细最新版的相关资料,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • springboot 接收List 入参的几种方法

    springboot 接收List 入参的几种方法

    本文主要介绍了springboot 接收List 入参的几种方法,本文主要介绍了7种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • idea在运行期间,实现让修改的页面实时生效

    idea在运行期间,实现让修改的页面实时生效

    这篇文章主要介绍了idea在运行期间,实现让修改的页面实时生效问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 如何基于java随机获取不重复数值

    如何基于java随机获取不重复数值

    这篇文章主要介绍了如何基于java随机获取不重复数值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • IDEA 如何控制编辑左侧的功能图标ICON(操作步骤)

    IDEA 如何控制编辑左侧的功能图标ICON(操作步骤)

    很多朋友被idea左侧的图标不见了这一问题搞的焦头烂额,不知道该怎么操作,今天小编就交大家如何控制编辑左侧的功能图标 ICON,文字内容不多,主要通过两张截图给大家说明,感兴趣的朋友一起看看吧
    2021-05-05

最新评论