Web 服務器本質是「請求-處理-響應」。Servlet 容器負責管理 Servlet 實例生命週期、解析 HTTP 請求、調度線程調用 `service/doGet/doPost`。本文從架構角色、核心介面、編譯部署、初始化流程、線程模型五個維度,建立 Servlet 完整認知框架。
Client (Browser)
│ HTTP Request
▼
Web Server (Tomcat: Coyote HTTP/1.1 Connector)
│ 解析請求 → HttpServletRequest
▼
Servlet Container (Catalina)
│ 查找/創建 Servlet 實例
│ 分配線程 → service(req, resp)
▼
Servlet Instance (你的業務類)
│ 業務邏輯 → 操作 req/resp
▼
Servlet Container
│ 根據 HttpServletResponse 生成 HTTP Response
▼
Web Server → Network → Client
| 介面/類 | 包 | 角色 | 關鍵方法 |
|---|---|---|---|
| `Servlet` | `javax.servlet` | 核心契約 | `init()`, `service()`, `destroy()`, `getServletConfig()`, `getServletInfo()` |
| `GenericServlet` | `javax.servlet` | 協議無關基類 | 實現 `Servlet`、`ServletConfig`,簡化 `init`/`destroy` |
| `HttpServlet` | `javax.servlet.http` | HTTP 專用基類 | `doGet()`, `doPost()`, `doPut()`, `doDelete()`, `service()` 分發 |
| `HttpServletRequest` | `javax.servlet.http` | 請求封裝 | 參數、Header、Cookie、Session、屬性、輸入流 |
| `HttpServletResponse` | `javax.servlet.http` | 響應封裝 | 狀態碼、Header、Cookie、輸出流、重定向 |
| `ServletConfig` | `javax.servlet` | 單個 Servlet 配置 | `getInitParameter()`, `getServletName()`, `getServletContext()` |
| `ServletContext` | `javax.servlet` | Web 應用全局上下文 | `getInitParameter()`, `getAttribute()`, `getResourceAsStream()`, `getRequestDispatcher()` |
[類加載] → [實例化 new Servlet()] → [init(config)] ──→ [服務中...] ──→ [destroy()] → [卸載]
│ ▲ │
│ │ │
└────────── 單例 ──────────┘ │
(多線程共享實例) │
(容器關閉/重載)
TimeServlet
com.example.TimeServlet
timezone
GMT+8
1
TimeServlet
/time
@WebServlet(
name = "TimeServlet",
urlPatterns = {"/time", "/current-time"},
initParams = {
@WebInitParam(name = "timezone", value = "GMT+8")
},
loadOnStartup = 1,
asyncSupported = true // 支持異步處理
)
public class TimeServlet extends HttpServlet {
@Override
public void init(ServletConfig config) {
String tz = config.getInitParameter("timezone");
// 初始化邏輯
}
// ...
}
| 模式 | 示例 | 匹配路徑 |
|---|---|---|
| 精確路徑 | `/api/users` | 僅 `/api/users` |
| 目錄前綴 | `/api/*` | `/api/users`, `/api/orders/123` |
| 擴展名 | `*.do`, `*.action` | `/login.do`, `/submit.action` |
| 默認 Servlet | `/` | 所有未匹配路徑 (靜態資源、SPA 入口) |
private User currentUser;` 多線程競爭覆蓋,**嚴禁**存儲請求狀態。
// ❌ 危險:成員變量存請求狀態
private String username;
public void doGet(...) { this.username = req.getParameter("u"); ... }
// ✅ 安全:局部變量
public void doGet(...) { String username = req.getParameter("u"); ... }
// ✅ 安全:請求屬性 (轉發鏈共享)
req.setAttribute("username", username);
// ✅ 安全:ThreadLocal (慎用,防洩漏)
private static final ThreadLocal currentUser = new ThreadLocal<>();
public void doGet(...) { currentUser.set(user); try {...} finally { currentUser.remove(); } }
| 範圍 | 配置方式 | 獲取 API | 用途 |
|---|---|---|---|
| 單 Servlet | ` | `config.getInitParameter(name)` | 該 Servlet 專用配置 (如時區、頁面大小) |
| 全 Web 應用 | ` | `context.getInitParameter(name)` | 全局配置 (如資料庫 URL、上傳目錄、功能開關) |
長耗時操作 (遠程調用、推送、報表生成) 釋放容器線程,提升吞吐:
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
AsyncContext ctx = req.startAsync(); // 開啟異步
ctx.setTimeout(30000);
new Thread(() -> {
try {
// 耗時業務
Thread.sleep(5000);
resp.setContentType("text/plain");
resp.getWriter().write("Async Done");
} catch (Exception e) { log.error(e); }
finally { ctx.complete(); } // 完成響應
}).start();
}
}
Servlet 看似簡單,實則蘊含「容器管理生命週期、單例多線程服務、請求響應封裝、配置與註解雙軌、同步與異步並存」等核心架構思想。徹底理解生命週期、線程模型、部署配置,是進階 Spring MVC、Spring WebFlux、Netty、gRPC 等框架的必經之路。下一篇將實戰《Filter 與 Listener:請求攔截鏈與應用生命週期監聽完全指南》。