前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ClassLoader#getResource与Class#getResource的差别

ClassLoader#getResource与Class#getResource的差别

作者头像
码农戏码
发布2022-11-18 09:45:49
3200
发布2022-11-18 09:45:49
举报
文章被收录于专栏:DDDDDD

JVM加载配置资源文件有两种方式:

1、ClassLoader#getResource

2、Class#getResource

两者之间的区别:

ClassLoader并不关心当前类的包名路径,它永远以classpath为基点来定位资源。需要注意的是在用ClassLoader加载资源时,路径不要以"/"开头,所有以"/"开头的路径都返回null

Class.getResource如果资源名是绝对路径(以"/"开头),那么会以classpath为基准路径去加载资源,如果不以"/"开头,那么以这个类的Class文件所在的路径为基准路径去加载资源

从源代码层次分析一下,这个结论对不对?

ClassLoader#getResource

代码语言:javascript
复制
public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }
代码语言:javascript
复制
 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;
    }

相对路径

代码语言:javascript
复制
//classloader从根节点开始查找
final URL resource = Thread.currentThread().getContextClassLoader().getResource("root.properties");
System.err.println(resource);
Assertions.assertNotNull(resource);

//成功获取

这样子是正常获取到资源

绝对路径

代码语言:javascript
复制
//以目录作对比,这样写,应该也没问题,但为什么返回是null呢?
//并且很多资料都直接说 classloader加载资源时,不要以 / 开头,以 / 开头都会返回null
final URL resource1 = Thread.currentThread().getContextClassLoader().getResource("/root.properties");
System.err.println(resource1);
Assertions.assertNull(resource1);

很多资料的结论classloader加载资源时,不要以 / 开头,以 / 开头都会返回null,是正确的。

从debug中可以看出来为什么以/开头,获取不到对应的资源。

主要还是对根节点的理解不一样:

classcloader以根节点去查找,是以当前的classpath为起点;

而以 / 开头,就变成类似root下了,自然查找不到

相对路径

代码语言:javascript
复制
final URL resource2 = Thread.currentThread().getContextClassLoader().getResource("./root.properties");
System.err.println(resource2);

这样是可以成功查找。

Class#getResouce

代码语言:javascript
复制
public InputStream getResourceAsStream(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
    }

看着Class#getResource,多加了一步resolveName,其实还是使用了Classloader#getResource方法

其中resolveName()

name不以'/'开头时,默认是从此类所在的包下取资源;

name以'/'开头时,则会substring(1),踢掉/,绝对路径转变成相对数据再从ClassPath根下获取资源

代码语言:javascript
复制
private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {
            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 {
            name = name.substring(1);
        }
        return name;
    }

在与com.zhuxingsheng.lang.GetResourceTest同目录下面创建package.properties

直接写文件名

代码语言:javascript
复制

final URL resource = GetResourceTest.class.getResource("package.properties");
System.err.println(resource);

//获取资源正常

根据debug信息,可以看出会从当前类目录去查找

绝对路径

代码语言:javascript
复制
final URL resource1 = getClass().getResource("/com/zhuxingsheng/lang/package.properties");
System.err.println(resource1);

//获取资源正常

从resolveName方法,可知,写了绝对路径,会被substring(1),也就是手动拼接完事包路径,走了resolveName的第一个不以 / 开头的分支路径。

也就是classloader#getResource不要写绝对路径。

完整相对路径

代码语言:javascript
复制

final URL resource1 = getClass().getResource("com/zhuxingsheng/lang/package.properties");
System.err.println(resource1);

//获取失败

从debug中可以看出,就是把完整的路径拼接了两次,路径变成了com/zhuxingsheng/lang/com/zhuxingsheng/lang/package.properties

结论

经过源代码的debug,上文的结论是正确的。

ClassLoader并不关心当前类的包名路径,它永远以classpath为基点来定位资源。需要注意的是在用ClassLoader加载资源时,路径不要以"/"开头,所有以"/"开头的路径都返回null

Class.getResource如果资源名是绝对路径(以"/"开头),那么会以classpath为基准路径去加载资源,如果不以"/"开头,那么以这个类的Class文件所在的路径为基准路径去加载资源

但在springboot中,自定义了classloader,打破了上述规则。下篇再看springboot的加载机制。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农戏码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 两者之间的区别:
  • ClassLoader#getResource
    • 相对路径
      • 绝对路径
        • 相对路径
        • Class#getResouce
          • 直接写文件名
            • 绝对路径
              • 完整相对路径
              • 结论
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档