Java Classloader基础及应用介绍

Java Classloader基础及应用介绍,第1张

Java Classloader基础及应用介绍

文章目录

一 what is Classloader二 内置Classloader

2.1 Bootstrap Classloader2.2 ExtClassloader2.3 AppClassloader2.4 小结 三 自定义`ClassLoader`

3.1 单亲委派3.2 如何绕过单亲委派3.3
本篇系统介绍class loader 及其应用.

一 what is Classloader

笔者以为,在讨论计算机(编程) 中的概念时首先一定要确定 概念所在的维度(范畴)是什么,否则会发现,大家说的貌似都对, 但哪里总有点不对.

当我们说 Java Classloader时,我们要搞清楚是说 Classloader 这个类/对象呢, 还是 Classloader机制.

二 内置Classloader

这个图是网上泛滥的, 面试说烂了的 Classloader 继承机制,学名叫做 双亲委派. 这个翻译很囧, 英文其实是parent delegating , 本意是 单亲委派…
这里留个坑,后面我们继续讲 怎么delegating.

2.1 Bootstrap Classloader

Bootstrap Classloader是Classloader体系中的顶层加载器. “顶层"的意思就是说” 我是祖先".
Classloader体系中的 Bootstrap Classloader 和 Object类作为所有的类的顶级父类, 是有着异曲同工之妙的.

Bootstrap Classloader是使用C++写的, 负责JVM最核心类库的加载,比如 java.lang 包, e.g., String.class. 可以通过-xbootclasspath指定Bootstrap Classloader的路径,也能通过系统属性得知当前Bootstrap Classloader加载了哪些资源.

有意思的是 ,Bootstrap Classloader 是没法从JVM中拿到引用的.

 public static void main(String[] args) {
        // BootstrapClassloader=>null 根类加载器是获取不到引用的
        System.out.println("BootstrapCL=>" + String.class.getClassLoader());

        // E:Javajdk1.8.0_121jrelibresources.jar;
        // E:Javajdk1.8.0_121jrelibrt.jar;
        // E:Javajdk1.8.0_121jrelibsunrsasign.jar;
        // E:Javajdk1.8.0_121jrelibjsse.jar;
        // E:Javajdk1.8.0_121jrelibjce.jar;
        // E:Javajdk1.8.0_121jrelibcharsets.jar;
        // E:Javajdk1.8.0_121jrelibjfr.jar;
        // E:Javajdk1.8.0_121jreclasses
        System.out.println(System.getProperty("sun.boot.class.path"));
		 // C:WINDOWSSunJavalibext
        System.out.println(System.getProperty("java.ext.dirs"));
    
       // sun.misc.Launcher$AppClassLoader@18b4aac2
        System.out.println(CL.class.getClassLoader());
    }
2.2 ExtClassloader

ExtClassloader用来加载 JAVA_HOME下的 jrelibext库,类似的, 可以通过 java.ext.dirs获取路径.

我们可以做一个小测试,写一个最简单的Hello world, 打成jar,然后将该jar 放到
JAVA_HOME下的 jrelibext下, 再跑下列的main ,会发现 是ExtClassLoader; 如果 再把这个jar删除了, 又是AppClassLoader.

public static void main(String[] args) throws Exception{
       // sun.misc.Launcher$ExtClassLoader@5f5a92bb
       ClassLoader loader = Class.forName("com.code.hello.Hello").getClassLoader();
       System.out.println(loader);

       // sun.misc.Launcher$AppClassLoader@18b4aac2
   }
2.3 AppClassloader

AppClassloader,也叫 SystemClassloader (系统classloader), 负责加载类路径下的类库资源,说白了就是你的应用的类路径. AppClassloader是自定义Classloader的父Classloader.

那怎么获取类路径呢? System.getProperty("java.class.path")

2.4 小结

前面说的三个大佬是内置ClassLoader, 实际在很多中间件中有自定义的ClassLoader,比如tomcat的ClassLoader就是个经典案例.

三 自定义ClassLoader

ClassLoader 是一个抽象类, 要实现自定义的ClassLoader ,只要 继承并override findClass方法即可,比如下面的代码.

这个MyCL类就是自定义的ClassLoader, 它从磁盘读取一个class 文件, 加载进JVM, 甚至还new 出了一个对象!

public class MyCL extends ClassLoader{
    // class文件的全路径名
    private String clazzFilePath;
    public MyCL(ClassLoader parent, String classFile) {
        super(parent);
        this.clazzFilePath = classFile;
    }
    public MyCL(String classFile) {
        this.clazzFilePath = classFile;
    }
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        byte[] clazzBytes;
        try {
            clazzBytes = Files.readAllBytes(Paths.get(clazzFilePath));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException("IO exception",e);
        }
        return defineClass(name, clazzBytes, 0, clazzBytes.length);
    }
    public static void main(String[] args) throws Exception{
        MyCL myCL = new MyCL("G:\github\code-snipptes\hello-world\target\classes\com\code\hello\Hello.class");
        Class helloClazz = myCL.findClass("com.code.hello.Hello");
        Object instance = helloClazz.newInstance();
        // com.code.hello.Hello@c39f790
        System.out.println(instance);
        //com.code.cl.MyCL@1ff8b8f
        System.out.println(instance.getClass().getClassLoader());
    }
}
3.1 单亲委派

为啥叫单亲委派,看看源码即知.
我们并没有看到 "双亲委派"中的"双亲"体现在哪里. 这种机制本质就是: 总是让父加载器优先去加载.

  protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // findLoadedClass()本质是个 native方法,用来记录当前这个加载器是不是已经加载过这个类
            Class c = findLoadedClass(name);
            // 这个类并没有加载过
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                   // 若父加载器存在, 则委派给 父加载器 会加载 
                   // 这里一定要理解下,  parent.loadClass()同样会执行一次 loadClass
                    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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    // ingore....
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
3.2 如何绕过单亲委派

假如有个HelloWorld.class ,默认由 AppClassLoader加载. 假如我想绕过去,即使用我MyCL去加载呢?
前面

3.3

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5712542.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存