最近做项目的时候遇到的一个资源加载问题,主要涉及到从工程(从IDE中启动项目)中加载资源文件(配置文件等)和将工程打包成jar包运行时加载资源文件的问题。
先看一下工程目录结构
config.properties就是我们需要加载的资源文件。
测试代码如下:
package com.li;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* Created by li on 2018/7/6.
*/
class test{}
public class ResourceTest {
public static void main(String[] args) throws IOException {
System.out.println("路径测试:");
System.out.println("XXX.class.getResource(\"/\")" + test.class.getResource("/"));
System.out.println("XXX.class.getResource(\"\")" + test.class.getResource(""));
System.out.println("XXX.class.getClassLoader().getResource(\"\")" + test.class.getClassLoader().getResource(""));
System.out.println("---------------------------------");
System.out.println("XXX.class.getResource(\"config.properties\")" + test.class.getResource("config.properties") );
System.out.println("XXX.class.getResource(\"/config.properties\")" + test.class.getResource("/config.properties") );
System.out.println("XXX.class.getClassLoader().getResource(\"config.properties\")" + test.class.getClassLoader().getResource("config.properties") );
System.out.println("输入流测试:");
InputStream inPkg = test.class.getResourceAsStream("");
if( inPkg == null ){
System.out.println("XXX.class.getResourceAsStream(\"\")" + inPkg);
}else{
System.out.println("XXX.class.getResourceAsStream(\"\")" + " the read result is:");
read(inPkg);
}
InputStream inRoot = test.class.getResourceAsStream("/");
if( inRoot == null ){
System.out.println("XXX.class.getResourceAsStream(\"/\");" + inRoot);
}else{
System.out.println("XXX.class.getResourceAsStream(\"/\")" + " the read result is:");
read(inRoot);
}
InputStream inPkgLoader = test.class.getClassLoader().getResourceAsStream("");
if( inPkgLoader == null ){
System.out.println( "XXX.class.getClassLoader().getResourceAsStream(\"\")" + inPkgLoader );
}else {
System.out.println( "XXX.class.getClassLoader().getResourceAsStream(\"\")" + " the read result is:" );
read(inPkgLoader);
}
System.out.println("-------------------------");
InputStream inPkg1 = test.class.getResourceAsStream("config.properties");
InputStream inRoot1 = test.class.getResourceAsStream("/config.properties");
InputStream inPkgLoader1 = test.class.getClassLoader().getResourceAsStream("config.properties");
if( inPkg1 == null ){
System.out.println( "XXX.class.getResourceAsStream(\"config.properties\")" + inPkg1 );
}else {
System.out.println( "XXX.class.getResourceAsStream(\"config.properties\")" + " the read result is:" );
read(inPkg1);
}
if( inRoot1 == null ){
System.out.println( "XXX.class.getResourceAsStream(\"/config.properties\")" + inRoot1 );
}else{
System.out.println( "XXX.class.getResourceAsStream(\"/config.properties\")" + " the read result is:");
read(inRoot1);
}
if( inPkgLoader1 == null ){
System.out.println( "test.class.getClassLoader().getResourceAsStream(\"config.properties\")" + inPkgLoader1 );
}else{
System.out.println( "test.class.getClassLoader().getResourceAsStream(\"config.properties\")" + " the read result is:");
read(inPkgLoader1);
}
}
public static void read(InputStream in) throws IOException {
byte[] buffer = new byte[1024];
ByteArrayOutputStream out = new ByteArrayOutputStream();
for( int len = 0 ; (len = in.read(buffer)) != -1; ){
out.write(buffer,0,len);
}
byte[] bs = out.toByteArray();
String str = new String(bs,0,bs.length, Charset.forName("utf-8"));
System.out.println(" " + str);
}
}
主要测试为方法为:
XXX.class.getResource();
XXX.class.getClassLoader().getResource();
XXX.class.getClassLoader().getResourceAsStream();
XXX.class.getResourceAsStream();
前提条件 在项目作为一个工程在文件夹下运行时(不打成jar包运行)
XXX.class.getResource("A.properties");
表示读取类XXX所属的包下名为A.properties的配置文件。
|--com
|--li
|--XXX.class
|--A.properties
XXX.class.getResource("/A.properties");
表示读取类XXX根路径下开始读取
|--com
|--li
|--XXX.class
|--A.properties
同理XXX.class.getResourceAsStream()方法。
接下来在谈一下
XXX.class.getClassLoader().getResource( resourcePath );
XXX.class.getClassLoader().getResourceAsStream( resourcePath );
当resourcePath以字符'/'开头时,返回结果为null
XXX.class.getClassLoader().getResource( resourcePath );
XXX.class.getClassLoader().getResourceAsStream( resourcePath );
均为从类的根路径开始读取资源。
接下来分几种情况来讨论一下以上几个方法具体的调用结果
1.直接在工程下运行:
输出结果如下:
路径测试:
XXX.class.getResource("/")file:/Users/li/workspace/PathTest/out/production/PathTest/
XXX.class.getResource("")file:/Users/li/workspace/PathTest/out/production/PathTest/com/li/
XXX.class.getClassLoader().getResource("")file:/Users/li/workspace/PathTest/out/production/PathTest/
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")file:/Users/li/workspace/PathTest/out/production/PathTest/config.properties
XXX.class.getClassLoader().getResource("config.properties")file:/Users/li/workspace/PathTest/out/production/PathTest/config.properties
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
ResourceTest.class
test.class
XXX.class.getResourceAsStream("/") the read result is:
com
config.properties
XXX.class.getClassLoader().getResourceAsStream("") the read result is:
com
config.properties
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties") the read result is:
inProject=true
test.class.getClassLoader().getResourceAsStream("config.properties") the read result is:
inProject=true
2.项目打成jar包,并且配置文件config.properties一并打入jar包中是结果:
路径测试:
XXX.class.getResource("/")null
XXX.class.getResource("")jar:file:/Users/li/cache/PathTest.jar!/com/li/
XXX.class.getClassLoader().getResource("")null
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")jar:file:/Users/li/cache/PathTest.jar!/config.properties
XXX.class.getClassLoader().getResource("config.properties")jar:file:/Users/li/cache/PathTest.jar!/config.properties
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
XXX.class.getResourceAsStream("/");null
XXX.class.getClassLoader().getResourceAsStream("")null
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties") the read result is:
inProject=true
test.class.getClassLoader().getResourceAsStream("config.properties") the read result is:
inProject=true
3.项目打成jar包,配置文件config.properties不打入jar包中,而是放在与jar包同一级别目录下:
路径测试:
XXX.class.getResource("/")null
XXX.class.getResource("")jar:file:/Users/li/cache/PathTest.jar!/com/li/
XXX.class.getClassLoader().getResource("")null
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")null
XXX.class.getClassLoader().getResource("config.properties")null
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
XXX.class.getResourceAsStream("/");null
XXX.class.getClassLoader().getResourceAsStream("")null
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties")null
test.class.getClassLoader().getResourceAsStream("config.properties")null
4.将jar包引入到项目中,并且项目的classpath下面放入config.properties配置文件,同时jar包内不放入config.properties配置文件:
路径测试:
XXX.class.getResource("/")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
XXX.class.getResource("")jar:file:/Users/li/workspace/PathTest1/lib/PathTest.jar!/com/li/
XXX.class.getClassLoader().getResource("")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")file:/Users/li/workspace/PathTest1/out/production/PathTest1/config.properties
XXX.class.getClassLoader().getResource("config.properties")file:/Users/li/workspace/PathTest1/out/production/PathTest1/config.properties
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
XXX.class.getResourceAsStream("/") the read result is:
ccom
config.properties
XXX.class.getClassLoader().getResourceAsStream("") the read result is:
ccom
config.properties
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties") the read result is:
outerPkg=true
test.class.getClassLoader().getResourceAsStream("config.properties") the read result is:
outerPkg=true
5.将jar包引入到项目中,并且项目的classpath下面放入config.properties配置文件,同时jar包内放入config.properties配置文件:
路径测试:
XXX.class.getResource("/")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
XXX.class.getResource("")jar:file:/Users/li/workspace/PathTest1/lib/PathTest.jar!/com/li/
XXX.class.getClassLoader().getResource("")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")file:/Users/li/workspace/PathTest1/out/production/PathTest1/config.properties
XXX.class.getClassLoader().getResource("config.properties")file:/Users/li/workspace/PathTest1/out/production/PathTest1/config.properties
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
XXX.class.getResourceAsStream("/") the read result is:
ccom
config.properties
XXX.class.getClassLoader().getResourceAsStream("") the read result is:
ccom
config.properties
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties") the read result is:
outerPkg=true
test.class.getClassLoader().getResourceAsStream("config.properties") the read result is:
outerPkg=true
6.将jar包引入到项目中,并且项目的classpath下面不放入config.properties配置文件,同时jar包内放入config.properties配置文件:
路径测试:
XXX.class.getResource("/")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
XXX.class.getResource("")jar:file:/Users/li/workspace/PathTest1/lib/PathTest.jar!/com/li/
XXX.class.getClassLoader().getResource("")file:/Users/li/workspace/PathTest1/out/production/PathTest1/
---------------------------------
XXX.class.getResource("config.properties")null
XXX.class.getResource("/config.properties")jar:file:/Users/li/workspace/PathTest1/lib/PathTest.jar!/config.properties
XXX.class.getClassLoader().getResource("config.properties")jar:file:/Users/li/workspace/PathTest1/lib/PathTest.jar!/config.properties
输入流测试:
XXX.class.getResourceAsStream("") the read result is:
XXX.class.getResourceAsStream("/") the read result is:
ccom
XXX.class.getClassLoader().getResourceAsStream("") the read result is:
ccom
-------------------------
XXX.class.getResourceAsStream("config.properties")null
XXX.class.getResourceAsStream("/config.properties") the read result is:
injarConfig=true
test.class.getClassLoader().getResourceAsStream("config.properties") the read result is:
injarConfig=true
ps: 当jar包内包含config.properties配置文件,而项目的classpath下面没有包含config.properties时候,getResource()或者getResourceAsStream()方法会去jar包下面寻找配置文件,但是如果项目的classpath下面存在config.properties时候,就会加载项目classpath下的配置文件。另外注意配置文件放入项目中读取时使用的是file:文件协议,而打包到jar后读取使用的是jar:协议。关于jar协议,之后还会有介绍,另外,当项目依赖A.jar和B.jar,同时在A.jar和B.jar中都包含配置文件config.properties是,只会加载到其中一个jar包中的配置文件,应该是按顺序遍历jar包查找配置文件,找到就返回(这一点未进行考究,不确定)。当然可以用XXX.class.getClassLoader().getResources("config.properties");方法获取jar包下的所有配置文件(考据了一下,spring中classpath*采用的这种方式)