说说Java中的资源文件的读取
最近在看spring的资源获取时发现JDK里存在几种不同方式的资源获取,因比较混乱特地总结起来帮助和我一样混乱的人理解。下面是我项目的类结构图,在 src/main/java
下有两个类 ResourceTest.java和Resource.java
,resources
目录下有两个资源文件 request.xml 和 conf/sysConf.json
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ ├── com
│ │ │ │ ├── alipay
│ │ │ │ │ ├── ResourceTest.java
│ │ │ │ │ └── Resource.java
│ │ └── resources
│ │ │ ├── conf
│ │ │ │ ├── sysConf.json
│ │ │ └── request.xml
└── local.iml
在ResourceTest中,我想获取Resource这个类以及request.xml、sysConf这两个资源文件,可以分为Class和ClassLoader两种方式来获取资源,而ClassLoader则又可以细分为3种方式:
public class ResourceTest {
public static void main(String[] args) {
// 1、通过Class的getResource方法
String a1 = ResourceTest.class.getResource("/com/alipay/Resource.class").getPath();
String a2 = ResourceTest.class.getResource("Resource.class").getPath();
String a3 = ResourceTest.class.getResource("/request.xml").getPath();
String a4 = ResourceTest.class.getResource("../../request.xml").getPath();
String a5 = ResourceTest.class.getResource("/conf/sysConf.json").getPath();
String a6 = ResourceTest.class.getResource("../../conf/sysConf.json").getPath();
// 2、通过本类的ClassLoader的getResource方法
String b1 = ResourceTest.class.getClassLoader().getResource("com/alipay/Resource.class").getPath();
String b2 = ResourceTest.class.getClassLoader().getResource("request.xml").getPath();
String b3 = ResourceTest.class.getClassLoader().getResource("conf/sysConf.json").getPath();
// 3、通过ClassLoader的getSystemResource方法
String c1 = ClassLoader.getSystemClassLoader().getResource("com/alipay/Resource.class").getPath();
String c2 = ClassLoader.getSystemClassLoader().getResource("request.xml").getPath();
String c3 = ClassLoader.getSystemClassLoader().getResource("conf/sysConf.json").getPath();
// 4、通过ClassLoader的getSystemResource方法
String d1 = ClassLoader.getSystemResource("com/alipay/Resource.class").getPath();
String d2 = ClassLoader.getSystemResource("request.xml").getPath();
String d3 = ClassLoader.getSystemResource("conf/sysConf.json").getPath();
// 5、通过Thread方式
String e1 = Thread.currentThread().getContextClassLoader().getResource("com/alipay/Resource.class").getPath();
String e2 = Thread.currentThread().getContextClassLoader().getResource("request.xml").getPath();
String e3 = Thread.currentThread().getContextClassLoader().getResource("conf/sysConf.json").getPath();
}
}
以上所有的方式都能够获取到对应的资源文件。
由于maven打包会把 src/main/java
和 src/main/resources
下的文件放到 target/classes
下,所以下面统一以根路径代表此目录,总结起来有以下几个规律:
- Class.getResource()的资源获取如果以
/
开头,则从根路径开始搜索资源。 - Class.getResource()的资源获取如果不以
/
开头,则从当前类所在的路径开始搜索资源。 - ClassLoader.getResource()的资源获取不能以
/
开头,统一从根路径开始搜索资源。
下面还是老习惯,翻开源码看看为什么是这样的规律。
Class.getResource()
public java.net.URL getResource(String name) {
name = resolveName(name);
// 获得类的类加载器,默认为AppClassLoader
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
可以很清晰的看出上面的资源获取流程:
- 解析文件路径,变成ClassLoader所支持的路径。
- 获取该类的类加载器,默认为AppClassLoader,接着调用它的getResource方法。
- 如果类加载器获取失败,直接走ClassLoader的getSystemResource方法来获取
我们看看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();
// 截取当前类所在的包和name使用 / 进行拼接
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
// 如果以 / 开头,截取 / 后面的内容
name = name.substring(1);
}
return name;
}
原理和我们上面分析的一样。因为Class的getResource最终还是调用的ClassLoader,所以我们接着来看ClassLoader的相关资源获取方法。
ClassLoader.getResource()
public URL getResource(String name) {
URL url;
if (parent != null) {
// 递归调用
url = parent.getResource(name);
} else {
// 使用BootstrapClassLoader发现资源
url = getBootstrapResource(name);
}
if (url == null) {
// 真正去找对应的url
url = findResource(name);
}
return url;
}
这个方法比较有意思的地方在于它使用了双亲委派机制来加载资源(回顾双亲委派机制 点我 ),它从BootstrapClassLoader一层层往下找直到最后找到该资源。本例中是通过AppClassLoader来找到了对应的资源(实际使用了URLClassLoader的findResource方法)
ClassLoader.getSystemResource()
这种方式对应于例子中的方式四,相比于方式三就多了一个空判断。
public static URL getSystemResource(String name) {
// systemClassLoader就是从Launcher获取的AppClassLoader
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);
}
getSystemResource和getResource的区别就在于你是否实现了自己的类加载器,如果都是使用的默认的AppClassLoader,这两个方法的作用一样。
线程上下文加载方式
这种加载方式对应于例子中的最后一种方式,它是使用 java.lang.Thread
中的方法 getContextClassLoader()
和 setContextClassLoader(ClassLoader cl)
用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)
方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是 AppClassLoader
,具体设置参考 sun.misc.Launcher
的构造函数。
更多关于线程上下文加载的疑问可以参考之前我的一篇文章:理解TCCL:线程上下文类加载器
因为线程上下文加载方式的灵活性,所以推荐在资源文件的读取时使用。
转自 http://benjaminwhx.com/2018/07/12/%E8%AF%B4%E8%AF%B4Java%E4%B8%AD%E7%9A%84%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6%E7%9A%84%E8%AF%BB%E5%8F%96/
说说Java中的资源文件的读取的更多相关文章
- java 中获得 资源文件方法
1 java 中获取资源文件的方法 项目目录如下 获取当前项目的目录路径 方法一:使用类名 MergeDocHandler.class.getClassLoader().getResource(&qu ...
- java基础知识3--如何获取资源文件(Java中获取资源文件的url)
java开发中,常见的resource文件有:.xml,.properties,.txt文件等,后台开发中经常用到读取资源文件,处理业务逻辑,然后返回结果. 获取资源文件的方法说明getResourc ...
- Java中的资源文件加载方式
文件加载方式有两种: 使用文件系统自带的路径机制,一个应用程序只能有一个当前目录,但可以有Path变量来访问多个目录 使用ClassPath路径机制,类路径跟Path全局变量一样也是有多个值 在Jav ...
- Java中获取资源文件的方法总结
这里总结3中方法获取资源文件的 ServletContext Class ClassLoader 文件的位置 1. ServletContext public void doGet(HttpServl ...
- java中io创建文件和读取文件
简单了解IO流:https://www.cnblogs.com/weibanggang/p/10034325.html package com.wbg.iodemo1128; import java. ...
- Jar中的Java程序如何读取Jar包中的资源文件
Jar中的Java程序如何读取Jar包中的资源文件 比如项目的组织结构如下(以idea中的项目为例): |-ProjectName |-.idea/ //这个目录是idea中项目的属性文件夹 |-s ...
- java 资源文件的读取
Java将配置文件当作一种资源(resource)来处理,并且提供了两个类来读取这些资源,一个是Class类,另一个是ClassLoader类. gradle 项目 项目目录结构 用Class类加载 ...
- 读取web工程中.properties资源文件的模板代码
读取web工程中.properties资源文件的模板代码 // 读取web工程中.properties资源文件的模板代码 private void test2() throws IOException ...
- Java加载资源文件的两种方法
处理配置文件对于Java程序员来说再常见不过了,不管是Servlet,Spring,抑或是Structs,都需要与配置文件打交道.Java将配置文件当作一种资源(resource)来处理,并且提供了两 ...
随机推荐
- CDK上安装kube-dashboard
首先把addon的代码下载下来 git clone https://github.com/minishift/minishift-addons 然后进入相应的目录,运行 ericdeMacBook-P ...
- iOS:UIView、UIControl、UIButton、UILabel简单的属性和方法常识
常见属性和方法 一 .UIVIew 常见属性 1.frame 位置和尺寸(以父控件的左上角为原点(0,0)) 2.center 中点 (以父控件的左上角为原点(0,0)) 3.bounds 位置和尺寸 ...
- AHB总线RAM Verilog实例
//*************************************************************************** // Copyright(c)2017, L ...
- Spark Streaming资源动态分配和动态控制消费速率
本篇从二个方面讲解: 高级特性: 1.Spark Streaming资源动态分配 2.Spark Streaming动态控制消费速率 原理剖析,动态控制消费速率其后面存在一套理论,资源动态分配也有一套 ...
- BS中保存参数
开发中经常需要将值存起来,当点击某一项时以便知道点击了哪一项. 一:应用JS页面跳转(牛腩中讲到) HTML: <td class="txt c"><a href ...
- 【转】The && and || Operator in JavaScript
原文: https://blog.mariusschulz.com/2016/05/25/the-andand-and-operator-in-javascript The && an ...
- [Erlang]Erlang经常使用工具解说
原创文章,转载请注明出处:服务器非业余研究http://blog.csdn.net/erlib 作者Sunface 联系邮箱:cto@188.com 但凡有图形界面的都须要linux系统安装了wx图形 ...
- 11g OCM 考试感悟
11g OCM 考试感悟 PrudentWoo 累,累.真的很累.考前每天全场景的刷两遍.三遍不觉得累.总感觉练习时间不够.考中尽管时间足够.可是压力很大.尤其看到一些和平时训练不一样题目的时候,那种 ...
- [读后感]spring Mvc 教程框架实例以及系统演示下载
[读后感]spring Mvc 教程框架实例以及系统演示下载 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&qu ...
- windows下ACE安装使用教程(转)
ACE简介:ACE是一个跨平台的用于并发通信的C++框架.它提供了丰富的C++封装器和框架组件.使用ACE,开发者可以开发出高性能,实时的通信服务和应用.ACE利用进程间通信,事件分离,动态链接和并发 ...