• 一文搞懂class.getClassLoader().getResource("xx.xml")和class.getResource("xx.xml")的区别和联系


    声明:本文转载,原文链接

    一文搞懂class.getClassLoader().getResource("xx.xml")和class.getResource("xx.xml")的区别和联系 - 简书  https://www.jianshu.com/p/e3c94eff77c5

    前言

    我们在阅读一些开源框架的时候,会发现有些开源框架在读取配置文件采用的是class.getClassLoader().getResource("xx.xml")这种方式,还有一些采用的是class.getResource("xx.xml")。那么此时我们可能就产生了一些疑问,这两种读取资源的方式有什么不同呢?现在我们就解析一下相关的源码,看看他们之间的区别和联系。

    类加载器科普

    我们知道JVM的类加载器是分为四大块的,分别是BootStrapClassLoader,extClassLoader以及AppClassLoader,最后就是我们自己自定义的ClassLoader。
    他们之间的父子关系通过下面的代码可以进行验证(自定义类加载器的父加载器是AppClassLoader,由于自定义类加载器不是本文的重点,就不加以验证,感兴趣的小伙伴可以自行验证)

    public class ResourceTest {
        public static void main(String[] args) {
            ClassLoader classLoader = ResourceTest.class.getClassLoader();
            //通过返回结果可以看到自己写的类默认是由applicationClassLoader加载的
            System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
            //类加载器是有父子关系的,appClassLoader的父加载器是extClassLoader
            classLoader = classLoader.getParent();
            System.out.println(classLoader);//sun.misc.Launcher$ExtClassLoader@61bbe9ba
            //类加载器是有父子关系的,extClassLoader的父加载器是BootStrapClassLoader,但是BootStrapClassLoader是C语言写的,所以会返回null
            classLoader = classLoader.getParent();
            System.out.println(classLoader);//null
        }
    }

    这时候我们可能就会有一个疑问,这些不同的类加载器都是加载哪些java文件呢?
    其实这些在sun.miscLauncher类里就有答案,现在我们打开这个类来看下。

    1.bootStrapClassLoader

    上图中红色箭头指定的路径就是bootstrapClassLoader类加载器可以加载的类的路径
    我们通过代码演示一下都有哪些类是属于bootStrapClassLoader来加载的

    public class ClassLoaderTest {
        public static void main(String[] args) {
            String str = System.getProperty("sun.boot.class.path");
            Arrays.stream(str.split(";")).forEach(s -> System.out.println(s));
            /*
            C:Program FilesJavajdk1.8.0_91jrelib
    esources.jar
            C:Program FilesJavajdk1.8.0_91jrelib
    t.jar
            C:Program FilesJavajdk1.8.0_91jrelibsunrsasign.jar
            C:Program FilesJavajdk1.8.0_91jrelibjsse.jar
            C:Program FilesJavajdk1.8.0_91jrelibjce.jar
            C:Program FilesJavajdk1.8.0_91jrelibcharsets.jar
            C:Program FilesJavajdk1.8.0_91jrelibjfr.jar
            C:Program FilesJavajdk1.8.0_91jreclasses
             */
        }
    }

    2.extClassLoader

    上面红色箭头指定的路径就是extClassLoader加载的路径,现在我们同样通过代码来看看这个类加载器主要加载哪些类。

    import java.util.Arrays;
    
    public class ClassLoaderTest {
        public static void main(String[] args) {
            String str = System.getProperty("java.ext.dirs");
            Arrays.stream(str.split(";")).forEach(s -> System.out.println(s));
            /*
            C:Program FilesJavajdk1.8.0_91jrelibext
            C:WINDOWSSunJavalibext
             */
        }
    }

    3.appClassLoader

    上面红色箭头就是appClassLoader加载的类的路径,这个加载器跟我们打的交道最多,因为我们写的类在不指定类加载器的情况下都是由它进行加载(正是因为如此,加入的第三方jar包也是由它加载,所以每个人在项目里加入的第三方jar包不同,加载的结果也不同,大家自行验证),下面我们同样通过代码去验证下它加载了哪些类。
    import java.util.Arrays;
    
    public class ClassLoaderTest {
        public static void main(String[] args) {
            String str = System.getProperty("java.class.path");
            Arrays.stream(str.split(";")).forEach(s -> System.out.println(s));
            /*
            C:Program FilesJavajdk1.8.0_91jrelibcharsets.jar
            C:Program FilesJavajdk1.8.0_91jrelibdeploy.jar
            C:Program FilesJavajdk1.8.0_91jrelibextaccess-bridge-64.jar
            C:Program FilesJavajdk1.8.0_91jrelibextcldrdata.jar
            C:Program FilesJavajdk1.8.0_91jrelibextdnsns.jar
            C:Program FilesJavajdk1.8.0_91jrelibextjaccess.jar
            C:Program FilesJavajdk1.8.0_91jrelibextjfxrt.jar
            C:Program FilesJavajdk1.8.0_91jrelibextlocaledata.jar
            C:Program FilesJavajdk1.8.0_91jrelibext
    ashorn.jar
            C:Program FilesJavajdk1.8.0_91jrelibextsunec.jar
            C:Program FilesJavajdk1.8.0_91jrelibextsunjce_provider.jar
            C:Program FilesJavajdk1.8.0_91jrelibextsunmscapi.jar
            C:Program FilesJavajdk1.8.0_91jrelibextsunpkcs11.jar
            C:Program FilesJavajdk1.8.0_91jrelibextzipfs.jar
            C:Program FilesJavajdk1.8.0_91jrelibjavaws.jar
            C:Program FilesJavajdk1.8.0_91jrelibjce.jar
            C:Program FilesJavajdk1.8.0_91jrelibjfr.jar
            C:Program FilesJavajdk1.8.0_91jrelibjfxswt.jar
            C:Program FilesJavajdk1.8.0_91jrelibjsse.jar
            C:Program FilesJavajdk1.8.0_91jrelibmanagement-agent.jar
            C:Program FilesJavajdk1.8.0_91jrelibplugin.jar
            C:Program FilesJavajdk1.8.0_91jrelib
    esources.jar
            C:Program FilesJavajdk1.8.0_91jrelib
    t.jar
            G:codeSelfStudyjdk_studyout	estjdk_study
            G:codeSelfStudyjdk_studyoutproductionjdk_study
            C:Users闫尚.m2
    epositoryjunitjunit4.12junit-4.12.jar
            C:Users闫尚.m2
    epositoryorghamcresthamcrest-core1.3hamcrest-core-1.3.jar
            C:Program FilesJavajdk1.8.0_91libdt.jar
            C:Program FilesJavajdk1.8.0_91lib	ools.jar
            C:Program FilesJavajdk1.8.0_91libsa-jdi.jar
            C:Program FilesJavajdk1.8.0_91libjconsole.jar
            C:Program FilesJavajdk1.8.0_91libpackager.jar
            C:Program FilesJavajdk1.8.0_91libjavafx-mx.jar
            C:Program FilesJavajdk1.8.0_91libant-javafx.jar
            G:softidealibidea_rt.jar
             */
        }
    }

    验证环境

    注意:这里主要是针对自定义类做的测试,自定义类默认的类加载器是appClassLoader,如果你用String等JDK自带的类做测试的话,因为所属的类加载器不同,debug的结果也是不同的,请自行验证)

    import java.net.URL;
    
    public class ResourceTest {
        public static void main(String[] args) {
           URL url = ResourceTest.class.getClassLoader().getResource("application.xml");
            System.out.println(url);
           url = ResourceTest.class.getResource("application.xml");
            System.out.println(url);
    /*
    *  结果;
    file:/G:/code/SelfStudy/jdk_study/out/production/jdk_study/application.xml
    file:/G:/code/SelfStudy/jdk_study/out/production/jdk_study/application.xml
    */
        }
    }

    源码分析

    有了上面所科普的类加载器的相关知识,那么我们就以验证环境的代码来看看class.getClassLoader().getResource("application.xml")和class.getResource("application.xml")的区别到底在哪。

    1.分析class.getClassLoader().getResource("application.xml")的源码

        public URL getResource(String name) {  //递归调用
            URL url;
            if (parent != null) {
                url = parent.getResource(name);
            } else {//递归调用获取BootStrapClassLoader的URL
                url = getBootstrapResource(name);
            }
            if (url == null) {
                url = findResource(name);
            }
            return url;
        }
    
    getClassLoader().getResource("application.xml")调用的就是上面的方法,这个方法其实是个递归调用,首先获取当前类加载器的父加载器,如果父加载器不为空,继续递归调用这个方法,直到递归到父加载器为空终止。然后就会调用getBootstrapResource(name)方法,现在我们来看下这个方法
       private static URL getBootstrapResource(String name) {
            URLClassPath ucp = getBootstrapClassPath();
            Resource res = ucp.getResource(name);
            return res != null ? res.getURL() : null;
        }
    

    getBootstrapResource这个方法又调用了getBootstrapClassPath()去获取路径,那么getBootstrapClassPath()是去哪里拿的路径呢?

      // Returns the URLClassPath that is used for finding system resources.
        static URLClassPath getBootstrapClassPath() {
            return sun.misc.Launcher.getBootstrapClassPath();
        }
    

    看到sun.misc.Launcher.getBootstrapClassPath() 是不是豁然开朗?这就是我们前面分析的bootStrapClassLoader加载的路径啊!

    OK,现在我们得出第一个结论:
    getClassLoader().getResource("application.xml")首先会去根类加载器加载的路径下找application.xml文件。如果找到就直接返回application.xml对应的URL地址
    那如果在根类加载器加载的路径下没有找到application.xml会怎么样呢?我们接着分析:当getBootstrapResource(name) 返回null的时候会接着调用 findResource(name),我们来看看这个方法debug的结果,如果不会debugJDK源码可以参考我的另一篇文章IDEA搭建JDK源码阅读环境

    通过debug的结果我们发现如果bootStrapClassLoader加载的路径下没有找到application.xml就会去extClassLoader类加载器加载的路径下去找。

    OK,这里我们得出第二个结论:
    getClassLoader().getResource("application.xml")在根类加载器加载路径下找不到application.xml时就会去extClassLoader加载器加载的路径去找。
     
    因为这里是递归调用,extClassLoader类加载器路径下没有找到application.xml,我们猜测此时应该就是到appClassLoader类加载路径下去找了。那么根据debug的结果发现与我们的预期一致。下面贴图

    OK,此时我们得出第三个结论:

    getClassLoader().getResource("application.xml")在extClassLoader加载器加载路径下找不到application.xml时就会去appClassLoader加载器加载的路径去找。如果appClassLoader加载路径下再找不到,直接返回null。

    2.分析class.getResource("application.xml")的源码

        public java.net.URL getResource(String name) {
            name = resolveName(name);
            ClassLoader cl = getClassLoader0();//获取当前类的类加载器
            if (cl==null) {//bootStrapClassLoader
                // A system class.
                return ClassLoader.getSystemResource(name);
            }
            return cl.getResource(name);//调用的还是class.getClassLoader.getResouce(name)
        }
    
    通过源码可以看出class.getResource(name)底层还是调用了class.getClassLoader().getResource(name),只是多了一步resolveName(name)的方法,那么我们就看看这个方法主要是做了什么操作。
    private String resolveName(String name) {
            if (name == null) {//1.name = null  直接返回
                return name;
            }
            if (!name.startsWith("/")) {//2.1 name不以/开头
                Class<?> c = this;
                while (c.isArray()) {
                    c = c.getComponentType();
                }
                String baseName = c.getName();//类对象对应的类名
                int index = baseName.lastIndexOf('.');
                if (index != -1) {//若以.结尾
                    name = baseName.substring(0, index).replace('.', '/')
                        +"/"+name;
                }
            } else { //2.2 name以/开头 就截取/之后的字符串
                name = name.substring(1);
            }
            return name;
        }
    

    上面的代码一目了然,就是对name进行一系列的组装,组装完成后还是调用的class.getClassLoader().getResource(name)方法去查找对应的资源文件。

    OK,此时我们得出结论:
    通过resolveName(name)组装后得到的name参数值跟class.getClassLoader().getResource(name)中的name参数值一样的话,他们最后的输出结果没有任何区别。
     

    结束语

    通过源码的分析,我们就大概知道了class.getClassLoader().getResource("xx.xml")和class.getResource("xx.xml")的区别以及联系,因为时间有限,就不对其他情况进行分析验证了,大家可以通过上面源码分析的思路去自己验证,谢谢大家。



    作者:负重前行丶
    链接:https://www.jianshu.com/p/e3c94eff77c5
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    本文转载,原文链接

    一文搞懂class.getClassLoader().getResource("xx.xml")和class.getResource("xx.xml")的区别和联系 - 简书  https://www.jianshu.com/p/e3c94eff77c5

  • 相关阅读:
    代码示例_触摸屏驱动
    代码示例_中断下半部
    代码示例_mmap的实现
    代码示例_阻塞IO
    代码示例_LCD控制
    代码示例_平台总线
    驱动_I2c驱动框架
    驱动_Input输入子系统
    Windows切换桌面或窗口快捷键
    几何分布
  • 原文地址:https://www.cnblogs.com/tongongV/p/13866609.html
Copyright © 2020-2023  润新知