Class.getResourceAsStream()与ClassLoader.getResourceAsStream()的用法

Class.getResourceAsStream()ClassLoader.getResourceAsStream() 两个方法都是加载类路径下资源的方法。类路径下文件的表示方法分为两种:绝对路径、相对路径,针对不同的路径,两个方法是否都支持?

准备工作

本文示例使用 Maven 进行构建,使用 .properties

resources 目录下添加资源文件 root.properties,内容如下:

resource.path=/

resources 目录下添加子目录 demo,然后在子目录下添加资源文件 current.properties,内容如下:

resource.path=/demo

这样,根目录及子目录下分别创建了一个资源文件。

Class.getResourceAsStream()

demo 包下创建测试类。

public class ResourceDemo {

    public static void main(String[] args) throws Exception {
        loadResourceByClass("/root.properties", "使用绝对路径加载根目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClass("../root.properties", "使用相对路径加载根目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClass("/demo/current.properties", "使用绝对路径加载当前目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClass("current.properties", "使用相对路径加载当前目录下资源");
    }

    private static void loadResourceByClass(String url, String message) throws Exception {
        try {
            System.out.println(message);
            InputStream resource = ResourceDemo.class.getResourceAsStream(url);
            Properties properties = new Properties();
            properties.load(resource);
            properties.forEach((k, v) -> System.out.println(k + " => " + v));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印结果

使用绝对路径加载根目录下资源
resource.path => /
------------------------------------------
使用相对路径加载根目录下资源
resource.path => /
------------------------------------------
使用绝对路径加载当前目录下资源
resource.path => /demo
------------------------------------------
使用相对路径加载当前目录下资源
resource.path => /demo

总结

  • 支持绝对路径加载:绝对路径以 / 开头,从 classpath 根目录开始查找
  • 支持相对路径加载:相对路径不以 / 开头,从当前类所在目录开始查找

ClassLoader.getResourceAsStream()

ClassLoader 类中有实例方法 getResourceAsStream() 以及静态方法 getSystemResourceAsStream() 都可以用于资源加载,后者使用 ClassLoader.getSystemClassLoader() 得到系统的 ClassLoader 然后再调用 getResourceAsStream()

使用上述四个路径再次测试一下 ClassLoader 的加载。

public class ResourceDemo {
    public static void main(String[] args) throws Exception {
        loadResourceByClassLoader("/root.properties", "使用绝对路径加载根目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClassLoader("../root.properties", "使用相对路径加载根目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClassLoader("/demo/current.properties", "使用绝对路径加载当前目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClassLoader("current.properties", "使用相对路径加载当前目录下资源");
    }

    private static void loadResourceByClassLoader(String url, String message) throws Exception {
        try {
            System.out.println(message);
            InputStream resource = ClassLoader.getSystemResourceAsStream(url);
            Properties properties = new Properties();
            properties.load(resource);
            properties.forEach((k, v) -> System.out.println(k + " => " + v));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果全部都报 NullPointerException,说明 Class.getResourceAsStream() 方法可以正常使用的路径对 ClassLoader 并不适用。ClassLoader.getSystemResourceAsStream() 方法支持的路径为:

  • 不支持以 / 开头的路径
  • 不以 / 开头的路径,也是从根目录开始查找

所以,要找到根目录下的 root.properties 以及 demo 目录下的 current.properties 两个文件,需要使用的路径分别为 root.propertiesdemo/current.properties

public class ResourceDemo {
    public static void main(String[] args) throws Exception {
        loadResourceByClassLoader("root.properties", "加载根目录下资源");
        System.out.println("------------------------------------------");
        loadResourceByClassLoader("demo/current.properties", "使用绝对路径加载当前目录下资源");
    }

    private static void loadResourceByClassLoader(String url, String message) throws Exception {
        try {
            System.out.println(message);
            InputStream resource = ClassLoader.getSystemResourceAsStream(url);
            Properties properties = new Properties();
            properties.load(resource);
            properties.forEach((k, v) -> System.out.println(k + " => " + v));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果如下

加载根目录下资源
resource.path => /
------------------------------------------
加载当前目录下资源
resource.path => /demo

总结

  • 不支持 / 开头的绝对路径
  • 不以 / 开头的路径,也是从 classpath 根目录开始查找

源码分析

其实 Class.getResourceAsStream() 方法最终也是使用的 ClassLoader 进行资源加载

public InputStream getResourceAsStream(String var1) {
    var1 = this.resolveName(var1);
    ClassLoader var2 = this.getClassLoader0();
    return var2 == null ? ClassLoader.getSystemResourceAsStream(var1) : var2.getResourceAsStream(var1);
}

但是,Class.getResourceAsStream() 方法与 ClassLoader.getSystemResourceAsStream() 方法支持的路径格式却不一样,其实就是 resolveName 方法进行了路径转换。

private String resolveName(String var1) {
    if (var1 == null) {
        return var1;
    } else {
        if (!var1.startsWith("/")) {
            // 相对路径,获取当前类路径,然后接上资源名
            Class var2;
            for(var2 = this; var2.isArray(); var2 = var2.getComponentType()) {
            }
            String var3 = var2.getName();
            int var4 = var3.lastIndexOf(46);
            if (var4 != -1) {
                var1 = var3.substring(0, var4).replace('.', '/') + "/" + var1;
            }
        } else {
            // 绝对路径,直接去掉开头的 `/`
            var1 = var1.substring(1);
        }
        return var1;
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值