核心 API 對照
| 代碼 | 返回內容 | 適用場景 |
MyClass.class.getResource("") | 當前類所在目錄 URI(不含類文件) | 定位同包資源 |
MyClass.class.getResource("/") | Classpath 根目錄 URI | 訪問 src 根下資源 |
Thread.currentThread().getContextClassLoader().getResource("") | 當前線程上下文 ClassLoader 的 Classpath 根目錄 | 推薦,兼容 Web 容器、線程池、OSGi |
MyClass.class.getClassLoader().getResource("") | 當前類加載器的 Classpath 根目錄 | 通用場景 |
ClassLoader.getSystemResource("") | 系統類加載器的 Classpath 根目錄 | 啟動類加載器資源 |
獲取 CLASSPATH 外資源(如專案根目錄、配置文件)
// 1. 先獲取 classpath 根 URL
URL base = MyClass.class.getResource("/"); // file:/D:/project/target/classes/
// 2. 相對路徑導航到目標目錄
File target = new File(base.getFile(), "../../config/app.properties");
// 3. 獲取規範絕對路徑
String canonicalPath = target.getCanonicalPath(); // D:/project/config/app.properties
Web 專案最佳實踐
1. 使用 ServletContext 獲取 WebRoot
String webRoot = servletContext.getRealPath("/"); // 絕對路徑,如 /var/www/app/
// 後續拼接相對路徑
File config = new File(webRoot, "WEB-INF/classes/config.properties");
2. Spring 註冊系統屬性(全局可訪問)
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>webapp.root</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
</listener>
啟動後可在任意處使用:System.getProperty("webapp.root")。
避坑指南
| 錯誤做法 | 後果 | 正確做法 |
硬編碼絕對路徑 D:/data/file.txt | 遷移部署即失效 | 動態獲取基準路徑 + 相對拼接 |
依賴 System.getProperty("user.dir") | 指向 JVM 啟動目錄(如 tomcat/bin),非專案目錄 | 用 ServletContext.getRealPath("/") 或 ClassLoader 定位 |
在 JAR 內用 File 讀取資源 | JAR 內無文件路徑,拋出 FileNotFoundException | 用 getResourceAsStream() 讀取輸入流 |
相對路徑 ../config 硬編碼 | 工作目錄變更導致失效 | 基於已知基準路徑(classpath/WebRoot)計算 |
統一資源訪問工具類
public class ResourceUtils {
public static URL getResource(String classpathRelativePath) {
return Thread.currentThread().getContextClassLoader().getResource(classpathRelativePath);
}
public static InputStream getResourceAsStream(String classpathRelativePath) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathRelativePath);
}
public static File getFile(String classpathRelativePath) throws IOException {
URL url = getResource(classpathRelativePath);
if (url == null) throw new FileNotFoundException(classpathRelativePath);
return new File(url.toURI());
}
}
總結
- 優先使用
Thread.currentThread().getContextClassLoader().getResource("") 獲取 classpath 根。
- Web 環境首選
ServletContext.getRealPath("/") 獲取 WebRoot。
- Spring 專案啟用
WebAppRootListener 註冊系統屬性,全局直達。
- 絕不硬編碼路徑;JAR 內資源必須用流讀取。
- 配置文件路徑外部化,通過環境變量或配置中心注入。