最近在看写Spring的源代码,里面有好多地方都用到了Class和ClassLoader类的getResource方法来加载资源文件。之前对这两个类的这个方法一知半解,概念也很模糊,这边做下整理,加深理解。

PS:本博客主要参考了Java中如何正确地从类路径中获取资源,但是为了加强理解记忆自己还是将其中的重点按照自己的风格排版记录了下,可以点击链接查看原文。

访问资源的主要方式

在Java中,通常可以通过以下方式来访问资源:

  • Class的getResource方法;
  • ClassLoader的getResource方法;
  • ClassLoader的getResources方法; //获取批量资源
  • ClassLoader的getSystemResource; //静态方法

在使用中,Class可通过直接引用类的class属性而获得,或是通过实例的 getClass() 方法来获得。获取ClassLoader的方式则比较多,常见以下几种:

  • 调用Class的getClassLoader方法,如:getClass().getClassLoader()
  • 由当前线程获取ClassLoader:Thread.currentThread().getContextClassLoader()
  • 获取系统ClassLoader: ClassLoader.getSystemClassLoader()

Class.getResource 与 ClassLoader.getResource 的区别

public class ClassLoaderAndClassContrast {

public static void main(String[] args) throws Exception {
Class<ClassLoaderAndClassContrast> aClass = ClassLoaderAndClassContrast.class;
ClassLoader classLoader = aClass.getClassLoader(); URL resource = classLoader.getResource("cookies.properties");
URL resource1 = aClass.getResource("dir/cookies.properties");
if(resource!=null){
byte[] bytes = FileCopyUtils.copyToByteArray(resource.openStream());
System.out.println(new String(bytes));
}
}
}

两者最大的区别,是从哪里开始寻找资源。

  • ClassLoader并不关心当前类的包名路径,它永远以classpath为基点来定位资源。需要注意的是在用ClassLoader加载资源时,路径不要以"/"开头,所有以"/"开头的路径都返回null;
  • Class.getResource如果资源名是绝对路径(以"/"开头),那么会以classpath为基准路径去加载资源,如果不以"/"开头,那么以这个类的Class文件所在的路径为基准路径去加载资源。

在实际开发过程中建议使用Class.getResource这个方法,但是如果你想获取批量资源,那么就必须使用到ClassLoader的getResources()方法。

public class ClassLoaderAndClassContrast {

    Class<ClassLoaderAndClassContrast> cls = ClassLoaderAndClassContrast.class;
ClassLoader ldr = cls.getClassLoader(); public static void println(Object s) {
System.out.println(s);
} void showResource(String name) {
println("## Test resource for: “" + name + "” ##");
println(String.format("ClassLoader#getResource(\"%s\")=%s", name, ldr.getResource(name)));
println(String.format("Class#getResource(\"%s\")=%s", name, cls.getResource(name)));
} public final void testForResource() throws Exception {
showResource("");
//对于Class来,返回这个类所在的路径
showResource("/");
showResource(cls.getSimpleName() + ".class");
String n = cls.getName().replace('.', '/') + ".class";
showResource(n);
showResource("/" + n);
showResource("java/lang/Object.class");
showResource("/java/lang/Object.class");
} public static void main(String[] args) throws Exception {
println("java.class.path: " + System.getProperty("java.class.path"));
println("user.dir: " + System.getProperty("user.dir"));
println("");
ClassLoaderAndClassContrast t = new ClassLoaderAndClassContrast();
t.testForResource();
}
}

正确使用getResource方法

  • 避免使用 Class.getResource("/") 或 ClassLoader.getResource("")。你应该传入一个确切的资源名,然后对输出结果作计算。比如,如果你确实想获取当前类是从哪个类路径起点上执行的,以前面提到的test.App来说,可以调用 App.class.getResource(App.class.getSimpleName() + ".class")。如果所得结果不是 jar 协议的URL,说明 class 文件没有打包,将所得结果去除尾部的 "test/App.class",即可获得 test.App 的类路径的起点;如果结果是 jar 协议的 URL,去除尾部的 "!/test/App.class",和前面的 "jar:",即是 test.App 所在的 jar 文件的url。
  • 如果要定位与某个类同一个包的资源,尽量使用那个类的getResource方法并使用相对路径。如前文所述,要获取与 test.App.class 同一个包下的 App.js 文件,应使用 App.class.getResource("App.js") 。当然,事无绝对,用 ClassLoader.getResource("test/App.js") 也可以,这取决于你所面对的问题是什么。
  • 如果对ClassLoader不太了解,那就尽量使用Class的getResource方法。
  • 如果不理解或无法确定该传给Class.getResource 方法的相对路径,那就以类路径的顶层包路径为参考起点,总是传给它以 "/" 开头的路径吧。
  • 不要假设你的调试环境就是最后的运行环境。你的代码可能不打包,也可能打包,你得考虑这些情况,不要埋坑。

获取批量资源

使用classLoader的getResources方法可以获得批量资源

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("META-INF/MANIFEST.MF");

Spring的ResourceLoader

在Spring框架中ResourceLoader和ResourcePatternResolver接口封装了获取资源的方法,我们可以直接拿来使用。ResourceUtils这个类中提供了很多判断资源类型的工具方法,可以直接使用。

//前面三种的写法效果是一样的,必须从classpath基准目录开始写精确的匹配路径
//如果想匹配多个资源,需要以classpath*/打头,然后配合通配符使用
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.spring.xml"); ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/beans.spring.xml"); ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/beans.spring.xml"); ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:/**/beans.spring.xml");
  • ResourceLoader 接口

  • ResourcePatternResolver 接口 --- 重点

给出一个例子

String txt = "";
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("templates/layout/email.html");
Resource resource = resources[0];
//获得文件流,因为在jar文件中,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流
InputStream stream = resource.getInputStream();
StringBuilder buffer = new StringBuilder();
byte[] bytes = new byte[1024];
try {
for (int n; (n = stream.read(bytes)) != -1; ) {
buffer.append(new String(bytes, 0, n));
}
} catch (IOException e) {
e.printStackTrace();
}
txt = buffer.toString();
  • ResourceUtils

  • ResourcePatternUtils

Class和ClassLoader的getResource方法对比的更多相关文章

  1. 使用Class.getResource和ClassLoader.getResource方法获取文件路径

    自从转投Java阵营后,一直发下Java程序的路径读取异常麻烦,因此查阅了比较多的版本内容,整合了一份自己的学习笔记.主要使用Class及通过ClassLoader来动态获取文件路径. 查阅链接如下: ...

  2. 【转载】使用Class.getResource和ClassLoader.getResource方法获取文件路径

    自从转投Java阵营后,一直发下Java程序的路径读取异常麻烦,因此查阅了比较多的版本内容,整合了一份自己的学习笔记.主要使用Class及通过ClassLoader来动态获取文件路径. 查阅链接如下: ...

  3. 浅谈getResource方法

    项目经常会读取一些配置文件, 因此getResource方法便能够起到重要作用 使用时主要是两种方法, 一个是字节码文件Class类, 另一个是ClassLoader类加载器 使用Class类时有两种 ...

  4. Linux和Windows下查看环境变量方法对比

    摘自:Linux和Windows下查看环境变量方法对比 一.查看所有环境变量的名称和值 Linux下:export Windows下:set 二.根据名称查该环境变量的值 Linux下:echo $环 ...

  5. C语言清空输入缓冲区的N种方法对比

    转自C语言清空输入缓冲区的N种方法对比 C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int get ...

  6. iPhone开发 数据持久化总结(终结篇)—5种数据持久化方法对比

    iPhone开发 数据持久化总结(终结篇)—5种数据持久化方法对比   iphoneiPhoneIPhoneIPHONEIphone数据持久化 对比总结 本篇对IOS中常用的5种数据持久化方法进行简单 ...

  7. interrupt interrupted isInterrupted 方法对比、区别与联系 多线程中篇(八)

    interrupt interrupted isInterrupted 是三个“长相”非常类似的方法. 本文将对这三个方法简单的对比下,首先了解下线程停止的方式 线程停止方式 在Java中如果想停止一 ...

  8. JavaScript中易混淆的DOM属性及方法对比

    JavaScript中易混淆的DOM属性及方法对比 ParentNode.children VS Node.prototype.childNodes ParentNode.children:该属性继承 ...

  9. vue-cli 项目优化之3种方法对比:本地静态库资源(推荐)、cdn、DllPlugin

    vue-cli 项目优化之3种方法对比:本地静态库资源(推荐).cdn.DllPlugin 事项 本地静态库资源 cdn DllPlugin 依赖 依赖cdn网站资源(有种完善方法:如果cdn引入不成 ...

随机推荐

  1. 使用命令行编译Qt程序

    code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...

  2. Ubuntu20.04linux内核(5.4.0版本)编译准备与实现过程-编译过程(2)

    前面因为博客园维修,所以内核编译过程一直没有发出来,现在把整个内核过程分享出来.本随笔给出内核的编译实现过程,在编译前需要参照我前面一篇随笔: Ubuntu20.04linux内核(5.4.0版本)编 ...

  3. Node.js 包管理器 NPM 讲解

    包管理器又称软件包管理系统,它是在电脑中自动安装.配制.卸载和升级软件包的工具组合,在各种系统软件和应用软件的安装管理中均有广泛应用.对于我们业务开发也很受益,相同的东西不必重复去造轮子. 每个工具或 ...

  4. 记一次metasploitable2内网渗透之8180端口tomcat

    扫描网段存活主机,确定内网metasploitable主机位置 nmap -T4 -sP 192.168.1.0/24 对目标主机进行扫描端口开放和系统信息 nmap -T4 -sV -Pn 192. ...

  5. Java中获取类的运行时结构

    获取运行时类的完整结构 通过反射获取运行时类的完整结构 Field(属性).Method(方法).Constructor(构造器).Superclass(父类).Interface(接口).Annot ...

  6. 解决SQLPLUS无法使用上下箭头

    1 问题描述 SQLPLUS中使用上下箭头无法获取历史命令,如下图所示: 按上下箭头会显示^[[A/^[[B. 2 解决方案 需要安装rlwrap,可以的话可以用包管理器安装,笔者环境CentOS,这 ...

  7. 《MySQL必知必会》学习笔记整理

    简介 此笔记只包含<MySQL必知必会>中部分章节的整理笔记.这部分章节主要是一些在<SQL必知必会>中并未讲解的独属于 MySQL 数据库的一些特性,如正则表达式.全文本搜索 ...

  8. Scrapy 爬虫项目框架

    1. Scrapy 简介 2. Scrapy 项目开发介绍 3. Scrapy 项目代码示例 3.1 setting.py:爬虫基本配置 3.2 items.py:定义您想抓取的数据 3.3 spid ...

  9. 033- while循环语句

    语法 初始化语句; while(boolean表达式) { 循环体语句; 控制条件语句; } 执行过程: 只要boolean表达式里面的结果是true,就会执行while大括号里面的语句,直到bool ...

  10. top:0;bottom:0;left:0;right:0;同时使用的效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...