getResource & getResourceAsStream

CLASSLOADER RESOURCE

Posted by gomyck on May 18, 2020

主要说明二者的区别以及使用上的注意事项

二者作用: 在当前 classpath 下, 获取资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo{
    public static void main(String[] params){
        String path = "";
        //todo 使用当前类的 Class 对象调用
        Demo.class.getResource(path);
        Demo.class.getResourceAsStream(path);
        //todo 使用当前类的加载器调用
        Demo.class.getClassLoader().getResource(path);
        Demo.class.getClassLoader().getResourceAsStream(path);
        //todo 使用当前线程的所在上下文的类加载器调用
        Thread.currentThread().getContextClassLoader().getResource("");
        Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
    }
}

上述代码为两个函数的常用使用方式, 这里不详细介绍, 百度有很详细的资料

区别

getResource 返回URI, 为资源标识符, 一般都为 file jar 可以使用返回对象的转换方法, 把资源转换为 stream

getResourceAsStream 返回流, 把指定路径的资源转换为流使用

注意事项

在 maven 项目中, 运行该 main 方法, classpath 为 target/classes

二者使用 Class 对象调用时, 区分 相对路径 & 绝对路径

1
2
3
4
5
6
7
//注意, 如果资源名(path)存在重复, 比如 "/com", 那么请使用 getResources
//因为当前 classpath 下还有 jar 包的目录结构是 com 开头的, 使用getResource只返回数组的0下标对象

//如果 path 是相对路径, 则以当前类所在位置, 以相对路径寻找, 如果是绝对路径, 那么从 classpath 向下寻找
Demo.class.getResource(path);
Demo.class.getResourceAsStream(path);

二者使用 ClassLoader 对象调用时, 必须使用相对路径, 否则返回 null

1
2
3
4
5
6
7
8
//注意, 如果资源名(path)存在重复, 比如 "/com", 那么请使用getResources
//因为当前 classpath 下还有 jar 包的目录结构是 com 开头的, 使用getResource只返回数组的0下标对象

//path 必须是相对路径, 且一定从 classpath 为基准向下寻找
Demo.class.getClassLoader().getResource(path);
Demo.class.getClassLoader().getResourceAsStream(path);
Thread.currentThread().getContextClassLoader().getResource(path);
Thread.currentThread().getContextClassLoader().getResourceAsStream(path);

个人理解:

Class 类调用, 是以类的维度获取资源, 所以可以使用相对路径, 且 / 为根目录(classpath)

ClassLoader 类调用, 是以类加载器的维度获取资源, 类加载器为 AppClassLoader, 是在当前 classpath 下加载类的, 所以如果以 ClassLoader为维度, 则一定是从 classpath 下寻找

源码分析:

Class 类调用, 实际逻辑和 classLoader 一样, 只不过在开始前, 加了一个函数: resolveName, 处理了绝对路径和相对路径 最终底层和 classLoader 调用的函数一致: getBootstrapResource