讓人迷惑的相對路徑
Java 中有一個很容易讓人誤解的問題, 就是路徑問題.
我們來看一段代碼.
項目路徑爲 /home/pathExample, 類的全限定名是 org.haoyifen.PathTest, 類的絕對地址爲 /home/pathExample/src/main/java/org/haoyifen/PathTest.java
public class PathTest {
@Test
public void relativePath() {
Path path = Paths.get("books.xml");
Path absolutePath = path.toAbsolutePath();
System.out.println(absolutePath);
}
}
初學者可能以爲上面輸出的 * /home/pathExample/src/main/java/org/haoyifen/books.xml* , 但其實輸出的是 * /home/pathExample/books.xml* , 也就是相對於項目目錄.
這說明 Java 類中處理相對路徑並不是以當前該類的路徑來進行相對的, 這一點不同於 HTML 中的 js 文件和 CSS 文件的引用.
原因
其實 Java 中處理相對路徑是以 JDK 運行時的屬性”user.dir” 來進行相對的. 我們可以改變”user.dir” 對應的值, 來改變相對路徑轉換爲絕對路徑的值.
@Test
public void changeUserDir() {
//將user.dir對應的值設定爲/home/haoyifen
System.setProperty("user.dir", "/home/haoyifen");
Path absolutePath = Paths.get("books.xml").toAbsolutePath();
//驗證相對路徑轉換爲絕對路徑時, 是以user.dir來進行計算的
Assert.assertEquals(absolutePath, Paths.get("/home/haoyifen/books.xml"));
}
在不同的運行環境中, 比如開發時的 IDE, 運行時的命令行或者 WEB 容器, JDK 運行時的屬性”user.dir” 的值的改變規則都是不同的, Java 中絕對不要使用相對路徑來讀取文件, 否則會出現開發時是正常的, 運行時就提示 java.io.FileNotFoundException: books.xml (沒有那個文件或目錄)
類路徑和類加載器
Java中加載文件, 最好是使用類路徑來進行加載, 以下是類路徑的一些規則 (假設類的全限定名爲org.haoyifen.PathTest, 類的根路徑爲/home/pathExample/target/classes)
- PathTest.class.getResource(“”)
當前類所在的目錄,file:/home/pathExample/target/classes/org/haoyifen/ - PathTest.class.getResource(“/”)
類的跟路徑, file:/home/pathExample/target/classes/ - PathTest.class.getClassLoader().getResource(“”)
類的跟路徑, file:/home/pathExample/target/classes/ - Thread.currentThread().getContextClassLoader().getResource(“”)
同上, 類的跟路徑, file:/home/pathExample/target/classes/
Spring 框架中的解決辦法
Spring 不僅僅適用於 Java SE, 也適用於 Java EE, Spring 中有個很重要的概念就是 Resource 接口, Resource 是一個抽象於底層實現的資源描述符, 用於獲取底層資源 (可以是文件系統的 File 也可以是網絡 URL) 的輸入流和其他相關信息. 其中使用的最多的實現就是 ClassPathResource, 常見的使用案例有:
- ClassPathXmlApplicationContext 加載 xml 配置文件啓動 Spring 框架
- Mybatis-Spring 中加載 Class 路徑中的 mapper.xml 文件來生成 DAO
Resource 最重要的方法如下:
*/
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
使用的就是 classLoader 來進行加載資源.
總結
- Java 中相對路徑是以 System.getProperty(“user.dir”) 來進行相對的.
- Java 中絕對不要使用相對路徑來加載文件, user.dir 設定規則在 IDE, 命令行和 WEB 容器中是不同的.
- 使用類路徑或者類加載器來加載文件, 這在 Java SE 項目和 WEB 容器中表現都是統一的.