本文為霍格沃茲測試學院優秀學員學習筆記。
在日常自動化測試開發工作中,經常要使用配置文件,進行環境配置,或進行數據驅動等。我們常常把這些文件放置在 resources
目錄下,然後通過 getResource
、ClassLoader.getResource
和 getResourceAsStream()
等方法去讀取。經常看到有不少同學在讀取配置文件時踩坑,本人也是一路踩坑摸索過來,這裡做一個簡要梳理,供大家參考。
一、何為 classpath ?
讀取資源文件最關鍵的就是找到文件的位置,歸根結底就是找路徑,而怎麼找,在哪找就是個問題。這其中和 classpath
有很大關係,因此我們先了解下 classpath
的概念,幫助理清思路。
我們用 Java 編寫的文件都是 .java
文件,而想要運行,還需將其編譯成 .class
字節碼文件才可被 JVM 運行;這就需要 JVM 先找到對應的 .class
才行,這也就是要找到對應的classpath
。
JVM 會在編譯項目時,會主動將 .java
文件編譯成 .class
文件 並和 resources
目錄下的靜態文件一起放在 target/classes
(如果是 test 下的類,便會放於 /target/test-classes
下)目錄下;
現有工程目錄如下:
編譯後進入 target
目錄下查看如下:
二、class.getResource()
先來看 getResource
的用法
先分別執行如下測試代碼,打印帶有"/"
和不帶"/"
的path
import org.junit.jupiter.api.Test;
public class ResourceTestDemo {
@Test
void getResourceTest(){
System.out.println(ResourceTestDemo.class.getResource(""));
System.out.println(ResourceTestDemo.class.getResource("/"));
}
打印結果:
file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/resourcetest/
file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/
結果分析:
1、getResource("")
不帶"/“時候是從當前類所在包路徑去獲取資源;
2、getResource("/")
帶”/"時候是從classpath
的根路徑獲取;
現在來嘗試獲取resources
下的文件2.txt
和3.txt
:
測試代碼:
@Test
void getResourceFileTest(){
System.out.println(ResourceTestDemo.class.getResource("/3.txt"));
System.out.println(ResourceTestDemo.class.getResource("/test/2.txt"));
}
打印結果:
file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/3.txt
file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/test/2.txt
三、getClassLoader().getResource()
和上述一樣,先分別執行測試代碼,打印帶有"/"
和不帶"/"
的path
:
@Test
void getClassLoaderResourceTest(){
System.out.println(ResourceTestDemo.class.getClassLoader().getResource(""));
System.out.println(ResourceTestDemo.class.getClassLoader().getResource("/"));
}
打印結果:
file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/
null
結果分析:
1、 getClassLoader().getResource("")
不帶"/“時候是從classpath
的根路徑獲取;
2、 getClassLoader().getResource("/")
帶有”/"打印為null
,路徑中無法帶有"/"
現在繼續嘗試獲取resources
下的文件2.txt
和3.txt
:
@Test
void getClassLoaderResourceFileTest(){
System.out.println(ResourceTestDemo.class.getClassLoader().getResource("3.txt"));
System.out.println(ResourceTestDemo.class.getClassLoader().getResource("test/2.txt"));
}
打印結果:
file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/3.txt
file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/test/2.txt
四、getResourceAsStream()
getResourceAsStream()
方法僅僅是獲取對應路徑文件的輸入流,在路徑的用法上與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);
}
其實這裡也是調用了getClassLoader
,只是方便了我們使用而已。
總結
class.getResource()
不帶"/"時候是從當前類所在包路徑去獲取資源;class.getResource()
帶"/"時候是從classpath
的根路徑獲取;class.getResource()
本質上也是調用了getClassLoader
,只是封裝了一層方便了我們使用而已;getClassLoader().getResource("")
不帶"/"時候是從classpath
的根路徑獲取;getClassLoader().getResource("/")
路徑中無法帶有"/"
;getResourceAsStream()
方法僅僅是獲取對應路徑文件的輸入流,在路徑的用法上與getResource()
一致;
以上,供大家探討。