Servlet 是 Java Web 開發的基石,掌握其請求處理、響應控制、資源加載、編碼處理、頁面跳轉等核心機制,是構建穩健 Web 應用的前提。本文系統整理 Servlet 核心知識點,涵蓋資源訪問差異、響應編碼最佳實踐、文件下載、請求參數獲取、轉發與重定向本質區別、路徑寫法規範等實戰高頻場景。
| 場景 | 訪問方式 | 核心 API | 優缺點 |
|---|---|---|---|
| Servlet 內部 | ServletContext | getServletContext().getResourceAsStream("/path") | 標準、支持大文件、實時讀取更新 |
| 普通 Java 類 | 類加載器 | Xxx.class.getClassLoader().getResourceAsStream("path") | 文件不能太大;類加載器緩存導致更新不生效 |
// 1. ServletContext 方式 (推薦用於 Web 資源)
InputStream is1 = getServletContext().getResourceAsStream("/WEB-INF/config.properties");
// 2. 類加載器方式 (適用於 classpath 資源)
InputStream is2 = UserDao.class.getClassLoader().getResourceAsStream("config.properties");
// 3. 類加載器緩存問題演示
// 修改 properties 文件後:
// 方式1:讀取到最新內容
// 方式2:仍讀取舊內容 (類已加載進內存,不會重新加載)
// 4. 靜態代碼塊單次加載優化
static {
// 僅首次加載類時執行,適合加載配置文件、初始化連接池
loadConfig();
}


// 方式一:分別設置 (最底層)
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 方式二:meta 標籤模擬響應頭 (JSP 頁面用)
out.write("");
// 方式三:一行搞定 (推薦,內部調用上述兩個方法)
response.setContentType("text/html;charset=UTF-8");

| 流類型 | 獲取方式 | 適用場景 | 注意 |
|---|---|---|---|
| PrintWriter | response.getWriter() | 字符輸出:HTML、JSON、文本 | 調用前必須先 setCharacterEncoding |
| ServletOutputStream | response.getOutputStream() | 字節輸出:文件下載、圖片、二進制 | 不能與 getWriter() 混用 |

// 強制瀏覽器彈出下載對話框,而非直接打開
response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
// 關鍵:中文文件名必須 URL 編碼
String encodedFileName = URLEncoder.encode(fileName, "UTF-8");
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String fileName = req.getParameter("fileName");
String realPath = getServletContext().getRealPath("/files/" + fileName);
File file = new File(realPath);
if (!file.exists()) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 1. 設置響應頭
resp.setContentType(getServletContext().getMimeType(fileName));
resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
resp.setContentLengthLong(file.length());
// 2. 流式傳輸 (避免大文件 OOM)
try (InputStream in = new BufferedInputStream(new FileInputStream(file));
OutputStream out = resp.getOutputStream()) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
}
}
String username = request.getParameter("username");
String[] hobbies = request.getParameterValues("hobby");
Enumerationnames = request.getParameterNames();
MapparamMap = request.getParameterMap();


| 維度 | 請求轉發 | 重定向 |
|---|---|---|
| API | request.getRequestDispatcher(path).forward(req, resp) | response.sendRedirect(path) |
| 請求次數 | 1 次 | 2 次 (服務端 302 → 瀏覽器新請求) |
| 地址欄變化 | 不變 | 變為目標 URL |
| request 域對象 | 共享同一 request | 新 request,數據丟失 |
| 訪問範圍 | 僅限當前 Web 應用內部 | 可跨應用、跨域、外部網站 |
| 路徑寫法 | 相對 Web 應用根目錄 | 相對容器根目錄 (需加項目名) |
| 響應提交狀態 | forward 前未提交可轉發;已提交拋異常 | 隨時可重定向 |
| 典型場景 | 登錄校驗失敗回登錄頁、MVC 邏輯跳轉 | 登錄成功跳首頁、支付完成跳結果頁、防表單重複提交 |
// 轉發
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
// 或
request.getRequestDispatcher("login.jsp").forward(request, response); // 相對當前路徑
// 重定向
response.sendRedirect(request.getContextPath() + "/index.jsp");
// 手動設置 302 (等效)
response.setStatus(302);
response.setHeader("Location", "/index.jsp");
關鍵點:轉發前 `response` 已提交內容會導致 `IllegalStateException`;轉發會清空 `response` 緩衝區但保留響應頭。

| 場景 | 參考基準 | 寫法示例 |
|---|---|---|
| 請求轉發 | Web 應用根目錄 | /WEB-INF/jsp/xxx.jsp 或 相對路徑 |
| 重定向 | 容器根目錄 (需項目名) | /projectName/xxx.jsp 或 ${pageContext.request.contextPath}/xxx.jsp |
| Servlet @WebServlet | Web 應用根目錄 | /user/login |
| jsp/include | Web 應用根目錄 | /header.jsp |
| form action / a href | 容器根目錄 (瀏覽器發請求) | /projectName/xxx.do |
| CSS/JS/圖片引用 | 容器根目錄 | ${pageContext.request.contextPath}/static/xxx.css |
@WebServlet("/user/show")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
User user = userService.findById(Integer.parseInt(id));
req.setAttribute("user", user);
req.getRequestDispatcher("/WEB-INF/jsp/user/detail.jsp").forward(req, resp);
return; // 關鍵!
}
}
Java Web 核心技術看似基礎,實則暗藏諸多坑點:資源訪問方式選擇影響配置熱更新;編碼設置順序決定中文是否亂碼;轉發重定向混用導致數據丟失或地址欄異常;路徑寫法不規範引發 404。建立「請求-響應-域對象-跳轉」完整心智模型,熟練掌握 `ServletContext`、`HttpServletRequest`、`HttpServletResponse` 三大核心對象的協作邊界,是從「能跑通」進階到「寫得穩、改得快、排錯準」的關鍵一步。