最近做項目的時候遇到的一個資源加載問題,主要涉及到從工程(從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*采用的這種方式)