• Classloader整理


    Classloader中文叫做类加载器,作用就是把编译好的class文件加载到jvm中。但是,在jvm启动的时候,并不是一次性全部加载的。

    三个原生的类加载器

    • Bootstrap ClassLoader最顶层的类加载器,主要加载核心类库,即jre lib下面的jar包。
    • Extention ClassLoader扩展类加载器,加载jre lib ext下的jar包。
    • AppClassLoader应用类加载器,加载当前应用即classpath下的所有类。

    AppClassLoader的父加载器(不是父类)是Extention ClassLoaderExtention ClassLoader的父加载器(不是父类)是Bootstrap ClassLoader
    我们可以写一些代码来验证一下。

        public static void main(String[] args) {
            System.out.println(User.class.getClassLoader());
            System.out.println(RequestDTO.class.getClassLoader().getParent());
            System.out.println(RequestDTO.class.getClassLoader().getParent().getParent());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中User类是我们自己创建的一个类,运行结果如下:

    sun.misc.Launcher$AppClassLoader@18b4aac2
    sun.misc.Launcher$ExtClassLoader@614ddd49
    null
    
    • 1
    • 2
    • 3

    前面两个结果是预期之内,最后一个获取Extention ClassLoader的结果为null是为什么呢?
    其实,Bootstrap ClassLoader是由C++编写的,它本身就是虚拟机的一部分,所以他并不是一个Java类,也就是无法在java代码中获取它的引用。

    双亲委托

    指的是类加载机制。
    AppClassLoader加载class的时候,它首先会去缓存里面查这个类是不是已经加载过了,如果没有,则让自己的父加载器去加载,一直递归,直到Bootstrap,如果Bootstrap ClassLoader也判断这个类没有被加载过,则会去jre lib下面去加载。如果还没加载到,则让自己子类Extention ClassLoader去jre lib ext下加载,如果它也加载不到,则让它的子类AppClassLoader去classpath下加载。总结一句话就是,由下向上委托,由上向下查找
    为什么是这样子可以从代码中体现。

    ClassLoader类的loadclass方法

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                //对应上面的先去缓存里面查找,底层是C的方法
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                        	//对应上面的让父加载器去查找,递归
                            c = parent.loadClass(name, false);
                        } else {
                        	//说明父加载器是Bootstrap ClassLoader,底层也是C的方法
                            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);
    
                        // 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;
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    自定义ClassLoader类

    • 所有自定义的ClassLoader类的父加载器默认都是AppClassLoader
    • 如果要自定义一个ClassLoader类,建议覆盖findClass()方法,而不要改写loadclass(),这样就可以保持双亲委派机制

    实现

    DiskClassLoader
    package com.curtain.classloader;
    
    import java.io.*;
    
    /**
     * @Author Curtain
     * @Date 2022/11/1 16:11
     * @Description
     */
    public class DiskClassLoader extends ClassLoader {
    
        private String mLibPath;
    
        public DiskClassLoader(String mLibPath) {
            this.mLibPath = mLibPath;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String fileName = getFileName(name);
            File file = new File(mLibPath, fileName);
            try{
            	//通过流将class文件读取进来,通过defineClass方法加载class类
                FileInputStream is = new FileInputStream(file);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                int len = 0;
                while ((len = is.read()) != -1){
                    bos.write(len);
                }
                byte[] bytes = bos.toByteArray();
                bos.close();
                is.close();
                return defineClass(name, bytes, 0, bytes.length);
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    
        private String getFileName(String name) {
            int index = name.lastIndexOf(".");
            if (index == -1) {
                return name + ".class";
            } else {
                return name.substring(index + 1) + ".class";
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    再写一个Test类,待会儿我们就用加载这个类。

    package com.curtain.classloader;
    
    /**
     * @Author Curtain
     * @Date 2022/11/1 14:42
     * @Description
     */
    public class Test {
        public void say(){
            System.out.println("Say Hello");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们编译好Test类,将 class文件放在一个地方,比如,D盘下的lib文件夹。
    最后就是测试类,DiskClassLoaderTest

    package com.curtain.classloader;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @Author Curtain
     * @Date 2022/11/1 16:19
     * @Description
     */
    public class DiskClassLoaderTest {
        public static void main(String[] args) {
            //创建自定义ClassLoader对象
            DiskClassLoader diskClassLoader = new DiskClassLoader("D:\\lib");
            try{
                //加载class文件
                Class c = diskClassLoader.loadClass("com.curtain.classloader.Test");
                if (c != null){
                    try{
                        Object o = c.newInstance();
                        Method method = c.getDeclaredMethod("say", null);
                        method.invoke(o, null);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    运行结果就是成功执行了Test类里面的say()方法

  • 相关阅读:
    如何快速实现OPC DA转为modbus TCP
    信息学奥赛一本通:1162:字符串逆序
    粗读DS-TransUNet: Dual Swin Transformer U-Net for Medical Image Segmentation
    00后程序员,2023年终总结
    kafka ack确认机制
    类似mac dock的tab切换组件
    京东api接口调用
    深入理解nodejs的异步IO与事件模块机制
    SV--对象拷贝、参数化的类
    Avoiding Row-by-Row Processing 避免逐行处理
  • 原文地址:https://blog.csdn.net/qq_41953872/article/details/127756829