| 類型 | 示例 | 適用場景 | 核心 API |
|---|---|---|---|
| URI 絕對路徑 | file:/D:/project/bin/aaa.class | 跨平台資源定位、ClassLoader 加載 | Class.getResource(), toURI() |
| 本地系統絕對路徑 | D:/project/bin/aaa.class | java.io 文件操作、本地文件系統交互 | File, FileInputStream |
| Classpath 相對路徑 | com/example/config.xml | 項目資源文件(配置、屬性、模板)訪問 | ClassLoader.getResource(), ResourceBundle |
| 用戶目錄相對路徑 | ./logs/app.log | 極不推薦,僅適用簡單獨立應用 | new File("."), System.getProperty("user.dir") |
java.io 包默認基於 user.dir(JVM 啟動工作目錄)解析相對路徑:
$TOMCAT_HOME/bin),導致文件讀寫失敗或寫入錯誤位置user.dir 不一致,不可移植getResourceAsStream() 支持 JAR 內資源)src/main/resources)天然映射到 Classpath 根
// 推薦:線程上下文類加載器,適應 Web 容器、OSGi、模塊化環境
String classpathUri = Thread.currentThread()
.getContextClassLoader()
.getResource("")
.toString(); // file:/D:/project/target/classes/ 或 jar:file:/path/app.jar!/BOOT-INF/classes/
// 當前類所在包目錄
String pkgUri = MyClass.class.getResource("").toString(); // file:/D:/project/target/classes/com/example/
// Classpath 根目錄
String rootUri = MyClass.class.getResource("/").toString(); // file:/D:/project/target/classes/
// JSP/Servlet 中
String webRoot = servletContext.getRealPath("/"); // 物理路徑:/opt/tomcat/webapps/myapp/
String resourcePath = webRoot + "WEB-INF/config/app.properties";
public class ResourceUtils {
public static InputStream getResourceAsStream(String classpathPath) {
// 1. 嘗試線程上下文類加載器(Web 容器、Spring Boot 標準)
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream(classpathPath);
if (is != null) return is;
// 2. 回退當前類加載器
is = ResourceUtils.class.getClassLoader().getResourceAsStream(classpathPath);
if (is != null) return is;
// 3. 回退系統類加載器
return ClassLoader.getSystemResourceAsStream(classpathPath);
}
// Web 環境下訪問 WEB-INF 外部資源
public static String getWebResourcePath(ServletContext ctx, String webRelativePath) {
return ctx.getRealPath(webRelativePath);
}
}
痛點:生產環境配置在 WEB-INF(Classpath 外),單元測試運行時無 ServletContext,無法用 getRealPath()。
| 方案 | 優點 | 缺點 |
|---|---|---|
測試時複製配置到 target/test-classes | 零代碼改動,複用生產邏輯 | 需構建腳本支持,易忘記同步 |
Spring FileSystemResource + Profile | 靈活指定文件系統路徑 | 代碼耦合環境感知邏輯 |
統一抽象 ResourceLoader 接口 | 解耦環境差異,可測試性強 | 需額外封裝層 |
| 測試配置放 Classpath,生產用外部覆蓋 | 符合 12-Factor App 原則 | 需容器化/部署腳本支持 |
@Configuration
@Profile("test")
public class TestConfig {
@Bean
public DataSource dataSource() {
// 讀取 classpath:test-db.properties
}
}
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public DataSource dataSource() {
// 讀取 file:${APP_HOME}/conf/db.properties
}
}
// File -> URI -> URL
File file = new File("D:/test.txt");
URI uri = file.toURI(); // file:/D:/test.txt
URL url = uri.toURL(); // file:/D:/test.txt
// URL -> URI -> File
URL url = new URL("file:/D:/test.txt");
URI uri = url.toURI(); // file:/D:/test.txt
File file = new File(uri); // new File("D:/test.txt")
// 注意:URL 的 file 協議在 Windows 路徑前有斜杠 /D:/...,File 構造器自動處理
ClassLoader.getResourceAsStream()File/Path (NIO.2)WEB-INF 外資源 → ServletContext.getRealPath() + 相對路徑./、../、無前綴)掌握 Java 路徑體系本質:所有相對路徑最終都由框架/容器解析為絕對路徑。選對基準路徑,隔離環境差異,是構建可移植、可測試、可維護 Java 應用的關鍵基礎設施。