在 Java Web 開發中,路徑問題始終是困擾開發者的經典難題:getRealPath() 返回 null、部署後找不到配置檔、前端資源 404、絕對/相對路徑混淆。本文系統整理了 Servlet 容器、JSP、前端 JavaScript、各類伺服器的路徑獲取方案,建立統一的路徑認知體系。
| 路徑類型 | 獲取方式 | 典型輸出範例 | 適用場景 |
|---|---|---|---|
| Web 應用根目錄 | getServletContext().getRealPath("/") | E:/workspace/project/ | 讀寫配置檔、生成報表、上傳檔案存儲 |
| CLASSPATH 根目錄 | getClass().getClassLoader().getResource("").getPath() | E:/workspace/project/WEB-INF/classes/ | 讀取類路徑下資源檔、屬性配置、Mapper XML |
| WebContent 目錄 | request.getServletContext().getRealPath("/") | E:/Workspaces/project/WebContent/ | JSP 中獲取部署後的 Web 根路徑 |
| 當前請求 URI | request.getRequestURI() | /project/module/page.jsp | 構建導航欄、權限攔截、日誌記錄 |
| 專案上下文路徑 | request.getContextPath() | /project | 前端資源引用、表單 action、AJAX URL |
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
String webRoot = context.getRealPath("/"); // Web 應用根目錄
String configPath = context.getRealPath("/WEB-INF/config.properties");
// 注意:某些容器(如 JBoss)可能返回 null,需用替代方案
}
// 方案一:類載入器(推薦)
String classPath = MyClass.class.getClassLoader().getResource("").getPath();
// 方案二:線程上下文類載入器(適配容器環境)
String ctxClassPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
// 方案三:系統類載入器
String sysClassPath = ClassLoader.getSystemResource("").getPath();
<%-- 1. 部署後的絕對磁碟路徑 --%>
<%= application.getRealPath("/") %>
<%-- 2. 專案上下文路徑(最常用) --%>
<%= request.getContextPath() %>
<%-- 3. 當前請求完整 URI --%>
<%= request.getRequestURI() %>
<%-- 4. 當前 JSP 檔案絕對路徑 --%>
<%= request.getRealPath(request.getServletPath()) %>
<%-- 5. 伺服器工作目錄(慎用) --%>
<%= new File(".").getAbsolutePath() %>
/ 開頭
request.getRequestDispatcher("/user/profile.jsp").forward(req, resp);
response.sendRedirect(request.getContextPath() + "/user/profile.jsp");
所有瀏覽器端相對路徑基準為 伺服器根目錄 (https://host:port/),非 Web 應用目錄。
<%-- 錯誤寫法 --%>
<link rel="stylesheet" href="css/style.css">
<form action="submit.do">
<%-- 正確寫法:強制加上下文路徑 --%>
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/style.css">
<form action="<%=request.getContextPath()%>/submit.do">
<%-- JSTL 簡寫 --%>
<c:url value="/css/style.css" var="cssUrl"/>
<link rel="stylesheet" href="${cssUrl}">
| 容器 | user.dir | 相對路徑基準 | 特殊注意事項 |
|---|---|---|---|
| Tomcat | %CATALINA_HOME%/bin | Web 應用根目錄 | 標準行為,getRealPath() 可靠 |
| WebLogic | WebLogic 安裝根目錄 | 域目錄 | 建議使用絕對路徑配置檔位置 |
| Resin | JSP 編譯臨時目錄 | Resin 安裝目錄 | new File("a.htm") 會在 Resin 安裝目錄建立檔案 |
| JBoss | JBoss 安裝目錄/bin | 臨時部署目錄 | getRealPath() 常返回 null,需用 VFS API |
public class PathUtil {
public static String getWebRootPath() {
// 1. 嘗試 ServletContext
String path = WebContextUtils.getServletContext().getRealPath("/");
if (path != null) return path;
// 2. 回退:類載入器定位 WEB-INF/classes
URL url = PathUtil.class.getClassLoader().getResource("");
if (url != null) {
String filePath = url.getPath();
int idx = filePath.indexOf("WEB-INF");
if (idx > 0) return filePath.substring(0, idx);
}
// 3. 最後手段:user.dir(僅適用 Tomcat 類容器)
return System.getProperty("user.dir");
}
}
function getRootPath() {
// 當前完整 URL:https://localhost:8080/myapp/page.jsp
var curWwwPath = window.document.location.href;
// 路徑部分:/myapp/page.jsp
var pathName = window.document.location.pathname;
// 專案名:/myapp
var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
// 主機地址:https://localhost:8080
var localhostPath = curWwwPath.substring(0, curWwwPath.indexOf(pathName));
return localhostPath + projectName; // https://localhost:8080/myapp
}
理解雙親委派模型對路徑獲取至關重要:
rt.jar 核心庫,Java 代碼無法直接訪問jre/lib/ext 擴展庫classpath 指定路徑,即我們常用的 ClassLoader.getSystemClassLoader()WEB-INF/classes 與 WEB-INF/libThread.currentThread().getContextClassLoader() 通常指向 WebApp ClassLoader,是容器環境下獲取資源的標準入口。
getResourceAsStream() 讀取,避免路徑依賴/data/uploads/),勿存入 Web 應用目錄${pageContext.request.contextPath} 或 <c:url>./、../ 在檔案遷移、重構時極易出錯getRealPath(),提高可移植性路徑問題本質上是「部署模型」與「開發模型」的不匹配。建立清晰的路徑分類認知,掌握各層級獲取 API,就能從根源上消除這類困擾。