核心差異速查表
| 特性 | 請求轉發 | 重定向 | 包含 |
| API | RequestDispatcher.forward() | HttpServletResponse.sendRedirect() | RequestDispatcher.include() |
| 瀏覽器地址欄 | 不變 | 變為目標 URL | 不變 |
| Request 域共享 | ✅ 同一請求對象 | ❌ 新請求對象 | ✅ 同一請求對象 |
| 跨應用/外部資源 | ❌ 僅同一 WebApp | ✅ 任意 URL | ❌ 僅同一 WebApp |
| 執行流程 | 伺服器內部跳轉 | 客戶端雙次請求 | 內嵌輸出到響應 |
| 典型場景 | MVC 控制器分派、登入攔截 | 登出、支付回調、防重複提交 | 頁面片段複用(Header/Footer) |
1. 請求轉發
Servlet 實現
@WebServlet("/dispatch")
public class DispatchServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 業務邏輯處理...
req.setAttribute("user", userService.findById(1));
// 轉發到 JSP 渲染
RequestDispatcher rd = req.getRequestDispatcher("/WEB-INF/views/detail.jsp");
rd.forward(req, resp);
}
}
JSP 標籤
<jsp:forward page="/WEB-INF/views/detail.jsp" />
關鍵點
- 目標資源必須在同一 Web 應用(
/ 代表應用上下文根)。
- 轉發前不可提交響應(如已調用
getWriter() 或 getOutputStream()),否則拋 IllegalStateException。
- 適用於 Controller → View 模式,隱藏內部結構。
2. 重定向
Servlet 實現
resp.sendRedirect(req.getContextPath() + "/login.jsp"); // 相對路徑自動補全
// 或絕對外部 URL
resp.sendRedirect("https://payment.example.com/callback");
關鍵點
- 瀏覽器收到 302 狀態碼 + Location 頭,發起第二次請求。
- 原請求的
request 域屬性全部丟失,需用 session、flash attribute 或 URL 參數傳遞資料。
- 適用於 POST-Redirect-GET 模式防止表單重複提交、OAuth 回調、跨域跳轉。
3. 包含
Servlet 實現
RequestDispatcher rd = req.getRequestDispatcher("/fragments/header.jsp");
rd.include(req, resp); // 輸出直接寫入當前響應體
rd = req.getRequestDispatcher("/fragments/footer.jsp");
rd.include(req, resp);
JSP 靜態/動態包含
<!-- 靜態包含:編譯期合併,適合不變片段 -->
<%@ include file="/fragments/header.jsp" %>
<!-- 動態包含:運行期處理,可傳參、支援條件判斷 -->
<jsp:include page="/fragments/nav.jsp">
<jsp:param name="highlight" value="home"/>
</jsp:include>
關鍵點
- 被包含頁面不應有
<html><body> 等根標籤,避免產生非法 HTML 結構。
- 包含頁面共享
request/response,可讀取屬性、寫入輸出流。
- 動態包含 (
jsp:include) 允許傳參,靜態包含 (@ include) 效能略高但無法傳參。
進階:Spring MVC 中的視圖解析
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String detail(@PathVariable Long id, Model model) {
model.addAttribute("user", userService.get(id));
return "user/detail"; // 邏輯視圖名,由 ViewResolver 解析為 /WEB-INF/views/user/detail.jsp
}
@PostMapping("/user")
public RedirectView create(UserForm form) {
User u = userService.save(form);
return new RedirectView("/user/" + u.getId(), true); // PRG 模式
}
}
總結選型建議
- 需隱藏內部路徑、共享請求數據 → 轉發
- 需改變瀏覽器 URL、防重複提交、跨應用跳轉 → 重定向
- 需組合頁面片段、複用 Header/Footer/側邊欄 → 包含