Class.getResource和ClassLoader.getResource区别与分析

零、 前言

在一个获取文件路径的代码内容上发现单元测试出现NullPointer, 但是这段代码却在服务器正常运行了两年多, 借此机会想认真探索下这两个方法的区别。



一、 区别

区别从代码输出结果来看比较清晰,

文件结构图: 

Class.getResource和ClassLoader.getResource区别与分析_第1张图片


以下为在多个场景下使用方式的代码:

package com.wenniuwuren.test;


/**
 * 获取文件路径
 * Created by wenniuwuren on 15/8/14.
 */
public class GetResourceTest {

    public static void main(String[] args) {


        // 带 “/” 的其实就是返回根目录
        System.out.println("GetResourceTest.class.getResource(\"/\") -----> "
                + GetResourceTest.class.getResource("/"));

        System.out.println("GetResourceTest.class.getResource(\"/DD.DD\") -----> "
                + GetResourceTest.class.getResource("/DD.DD"));

        // *************************************************

        // 返回当前路径
        System.out.println("GetResourceTest.class.getResource(\"\") -----> "
                + GetResourceTest.class.getResource(""));

        System.out.println("GetResourceTest.class.getResource(\"AA.AA\") -----> "
                + GetResourceTest.class.getResource("AA.AA"));

        System.out.println("GetResourceTest.class.getResource(\"subtest/BB.BB\") -----> "
                + GetResourceTest.class.getResource("subtest/BB.BB"));

        System.out.println("GetResourceTest.class.getResource(\"../CC.CC\") -----> "
                + GetResourceTest.class.getResource("../CC.CC"));



        // 查找classloader root。 用 getClassLoader 最容易出问题, 因为是根据类加载器来的, 类加载器多种多样可能在不同环境下产生不同结果
        GetResourceTest getResourceTest = new GetResourceTest();
        System.out.println("getResourceTest.getClass().getClassLoader().getResource(\"\") -----> "
                + getResourceTest.getClass().getClassLoader().getResource(""));

        System.out.println("getResourceTest.getClass().getClassLoader().getResource(\"/\") -----> "
                + getResourceTest.getClass().getClassLoader().getResource("/"));


        /**
         * 从输出结果来看总结上述: Class.class.getResource("/")  ==  Class.getClass().getClassLoader().getResource("")
         */


        // 这是一种可以避免在不同Web容器下获取路径异常的方法
        System.out.println("GetResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath() -----> "
                + GetResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
    }

}



二、 从源码角度分析
 
分析前先看个 Java 类加载的架构图, 有助于下文理解:
Class.getResource和ClassLoader.getResource区别与分析_第2张图片
1. Class.getResource
算法基本为: 搜索策略为此类关联的类加载器实现的, 即这个方法委托给这个对象的类加载器,如果对象是被 Bootstrap 类加载器加载的, 则进入 ClassLoader.getSystemResource() 方法, 这个方法先找系统级别的类加载器(这个加载器是一般应用启动的类加载器), 如果找不到则使用虚拟机内置的类加载器。
如果传的参数是“/”则返回根路径, 传的是具体文件名就返回文件绝对路径。
public java.net.URL getResource(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResource(name);
        }
        return cl.getResource(name);
    }



2. ClassLoader.getResource
会先从父加载器去找资源, 如果父加载器为 null 则从虚拟机内置加载器去查找(这步何上面最后一步相同), 如果还没有, 则去findResource() [可以重写这个方法自己实现个类加载器去寻找资源]去找。
public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }



你可能感兴趣的:(ClassLoader,相对路径,绝对路径,getresource,获取路径)