文档章节

Class#getResource与ClassLoader#getResource分析

叶易
 叶易
发布于 2016/08/09 17:07
字数 814
阅读 50
收藏 0
  • Class#getResource方法的参数path可以与以'/'开头的绝对路径或是不以'/'开头的相对路径,当以'/'开头时,会从classpath路径下获取资源,当不以'/'开头时,则从该类所在的包下获取资源,xxx.class.getResource() 即xxx.class类所在包。
  • 而ClassLoader#getResource方法的参数却不能以'/'开头,其是从classpath下面获取资源。

来看看下面代码的结果

package com.jdk.resource;
public class Resource {
	 public static void main(String[] args) throws Exception {
	        System.out.println(Resource.class.getResource(""));
	        System.out.println(Resource.class.getResource("/"));
	        System.out.println(Resource.class.getClassLoader().getResource(""));
	        System.out.println(Resource.class.getClassLoader().getResource("/"));
	    }
}

结果:

file:/D:/workspace/JDKCore/bin/com/jdk/resource/
file:/D:/workspace/JDKCore/bin/
file:/D:/workspace/JDKCore/bin/
null

可以看到Resource.class.getResource("")得到的路径classpath下Resource 类所在包,而Resource.class.getResource("/")为classpath根路径;而Resource.class.getClassLoader().getResource("")同样为classpath根路径,Resource.class.getClassLoader().getResource("/")则为空。 下面来看看Class#getResource方法的源码

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);
   }

从 cl.getResource(name)一行可以看出,Class#getResource方法最终还是调用了CalssLoader#getResource方法。再来看看name = resolveName(name)的实现,做了什么处理,其调用了Class类的resolveName方法

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;
    }

可以看出,resolveName方法的目的是当参数name以'/'开头就将'/'去除再返回,而当不以'/'开头则根据这个类对应的带包名全称变换成具体的路径名,如com.jdk.resource替换成com/jdk/resource。

现在应该明白最开始那个Resource 类中main方法的结果,实际上Class#getResource方法是去调CalssLoader#getResource的方法,只是在调用时会去判断是从相对路径还是绝对路径获取资源。不过api是有一点点坑Class#getResource以'/'开头与ClassLoader#getResource相同,再记住ClassLoader#getResource不能以'/'开头就行了。

有了前面这个Classr#getResource与ClassLoader#getResource的对比,Class#getResourceAsStream与ClassLoader#getResourceAsStream其实也就ok了。

Class#getResourceAsStream源码:

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);
    }

ClassLoader#getResourceAsStream源码:

public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }

所以,可以用下面三种方式得到资源

  1. 利用Class#getResourceAsStream方法根据绝对路径从classpath下面得到,参数path以'/'开头
  2. 利用Class#getResourceAsStream方法根据类所处包的相对路径得到,参数path不能以'/'开头
  3. 利用ClassLoader#getResourceAsStream方法根绝对路径从classpath下面得到,参数path不能以'/'开头

在com.jdk.resource目录下创建一个test.properties文件,可以用以下三种方式得到

package com.jdk.resource;
public class Resource {
	 public static void main(String[] args) throws Exception {
		    //第一种方式
		    System.out.println(Resource.class.getResourceAsStream("test.properties"));
		    //第二种方式
		   System.out.println(Resource.class.getResourceAsStream("/com/jdk/resource/test.properties"));
		    //第三种方式
	System.out.println(Resource.class.getClassLoader().getResourceAsStream("com/jdk/resource/test.properties"));
	    }
}

结果:

java.io.BufferedInputStream@659e0bfd
java.io.BufferedInputStream@2a139a55
java.io.BufferedInputStream@15db9742

这对查看,Spring Resource 接口的实现ClassPathResource类getInputStream方法有一定帮助

public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}

© 著作权归作者所有

共有 人打赏支持
叶易
粉丝 22
博文 24
码字总数 30959
作品 0
无锡
Java 加载资源文件

简介 Java中获取资源的最常用的2中方式就是使用Class的getResource和使用ClassLoader的getResource方法,当然还有它们相关的方法。这里就介绍一下使用这2中方式的区别,和它们搜索使用的路径...

trayvon
2016/12/03
53
0
如何针对业务场景做数据分析?

企业的数据分析是个很复杂的工程,需要业务和分析技术两块知识。这里从业务的角度切入,谈谈如何对业务分析。 首先,企业的分析主要分为管理分析和经营业务分析,分析整体的思路是:明确业务...

李启方
2017/05/23
0
0
SharePoint Server 2013 中的分析处理概述

要帮助确定和显示用户认为最有用和最相关的内容,SharePoint Server 2013 中的“分析处理组件”可自己分析这两种内容,还会分析用户与其进行交互的方法。分析结果将添加到搜索索引中的项目,...

hxyhxl
2014/09/28
0
0
你务必要搞清楚的六大数据分析知识点

一、数据分析是神马 1. 何谓数据分析 用适当的统计分析方法和相应工具,对收集来的大量数据进行详细研究和概括总结,提取有用信息和形成结论,这一过程叫做数据分析。 数据分析有广义和狭义之...

柯西带你学编程
05/22
0
0
数据分析方法论,流程和框架?

对于数据分析的方法论、流程和框架,我从一个十年大数据人的日常工作实践、可落地可实施纯实战的角度总结归纳分享给大家。(节选自小讲“数据分析师-从零入门到精通”) 第一部分:数据分析框...

王礼Leon
2017/08/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 小心着凉 @红薯

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子:5.33起,其声呜呜然,如怨如慕,如泣如诉。余音袅袅,不绝如缕。分享Arch Enemy的单曲《Bridge Of Destiny (2009)》 《Bridge Of...

小小编辑
27分钟前
34
0
what f,,

anlve
今天
2
0
初级开发-编程题

` public static void main(String[] args) { System.out.println(changeStrToUpperCase("user_name_abc")); System.out.println(changeStrToLowerCase(changeStrToUpperCase("user_name_abc......

小池仔
今天
11
0
现场看路演了!

HiBlock
昨天
20
0
Rabbit MQ基本概念介绍

RabbitMQ介绍 • RabbitMQ是一个消息中间件,是一个很好用的消息队列框架。 • ConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。Connection是RabbitMQ的s...

寰宇01
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部