在日常自動化測試開發中,經常要使用配置文件,或進行環境配置,或進行數據驅動等;我們常常把這些文件放置在
resources
目錄下,然後通過getResource
、ClassLoader.getResource
和getResourceAsStream()
等方法去讀取,經常看到有人在讀取配置文件時踩坑,本人也是踩坑過來了,這裏做梳理分享
1、何爲classpath
讀取資源文件最關鍵的就是找到文件的位置,歸根結底就是找路徑,而怎麼找,在哪找就是個問題;這其中和
classpath
有很大的關係,因此先了解下classpath
幫助理解
- 我們用
Java
編寫的文件都是.java
文件,而想要運行,還需將其編譯成.class
字節碼文件纔可被JVM
運行;這就需要JVM
先找到對應的.class
纔行,這也就是要找到對應的classpath
。 JVM
會在編譯項目時,會主動將.java
文件編譯成.class
文件 並和resources
目錄下的靜態文件一起放在target/classes
(如果是test
下的類,便會放於/target/test-classes
下)目錄下;
現有工程目錄如下:
編譯後進入target
目錄下查看如下:
2、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
3、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
4、getResourceAsStream()
getResourceAsStream()
方法僅僅是獲取對應路徑文件的輸入流,在路徑的用法上與getResource()
一致
5、補充
其實當我們查看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
,只是方便了我們使用而已
6、總結
class.getResource()
不帶"/"時候是從當前類所在包路徑去獲取資源class.getResource()
帶"/"時候是從classpath
的根路徑獲取class.getResource()
本質上也是調用了getClassLoader
,只是封裝了一層方便了我們使用而已getClassLoader().getResource("")
不帶"/"時候是從classpath
的根路徑獲取getClassLoader().getResource("/")
路徑中無法帶有"/"
getResourceAsStream()
方法僅僅是獲取對應路徑文件的輸入流,在路徑的用法上與getResource()
一致