Servlet 生命週期是 Java Web 面試必考、也是理解容器管理多線程請求的基石。本文通過代碼實戰、控制台日誌、瀏覽器效果圖,完整演示 init → service → doGet/doPost → destroy 的調用鏈與關鍵細節。
load-on-startup>0)或首次請求時,調用無參構造器創建唯一實例。init(ServletConfig):僅執行一次,完成資源獲取(數據源、配置、緩存預熱)。service(req, resp):每次請求由容器分配線程調用,默認分發到 doGet/doPost 等 doXxx 方法。destroy():容器關閉或應用重新部署前調用一次,釋放資源(關閉連接池、線程池、寫持久化)。<servlet> <servlet-name>LifeCycleServlet</servlet-name> <servlet-class>com.servlet.LifeCycleServlet</servlet-class> <load-on-startup>1</load-on-startup> <!-- 容器啟動時初始化 --> </servlet> <servlet-mapping> <servlet-name>LifeCycleServlet</servlet-name> <url-pattern>/servlet/LifeCycleServlet</url-pattern> </servlet-mapping>
package com.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LifeCycleServlet extends HttpServlet {
private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
public LifeCycleServlet() {
System.out.println("[" + df.format(new Date()) + "] 構造器");
}
@Override public void init() throws ServletException {
log("[" + df.format(new Date()) + "] init()");
}
@Override protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
log("[" + df.format(new Date()) + "] service() -> " + req.getMethod());
super.service(req, resp); // 分發到 doGet/doPost
}
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
log("[" + df.format(new Date()) + "] doGet()");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("Servlet 生命週期演示 (GET)
");
out.println("");
}
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
log("[" + df.format(new Date()) + "] doPost()");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("[10:00:00.123] 構造器 [10:00:00.125] init()

[10:00:05.456] service() -> GET [10:00:05.457] doGet()

[10:00:10.789] service() -> POST [10:00:10.790] doPost()
[10:05:00.000] destroy()

| 特性 | 說明 |
|---|---|
| 實例唯一性 | 全應用僅一個 Servlet 實例,多線程共享實例變量 → 線程不安全 |
| init 執行次數 | 僅 1 次(構造器後、首次 service 前) |
| service 執行次數 | 每請求 1 次,由容器線程池並發調用 |
| destroy 執行時機 | 容器正常關閉、應用重新部署、Servlet 移除 |
| 構造器 vs init | 構造器無 ServletConfig,init 可獲取初始化參數、ServletContext |
| 線程安全 | 不要在 Servlet 實例變量保存請求狀態,用局部變量、ThreadLocal、請求域 |
徹底理解生命週期與多線程模型,是編寫高性能、穩定 Servlet 應用的前提。