首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Servlet 生命週期完整解析:init、service、doGet/doPost、destroy 執行順序與多線程本質

     編輯:Java 綜合教學

Servlet 生命週期是 Java Web 面試必考、也是理解容器管理多線程請求的基石。本文通過代碼實戰、控制台日誌、瀏覽器效果圖,完整演示 init → service → doGet/doPost → destroy 的調用鏈與關鍵細節。

生命週期五大階段

  1. 加載與實例化:容器啟動(load-on-startup>0)或首次請求時,調用無參構造器創建唯一實例。
  2. 初始化 init(ServletConfig):僅執行一次,完成資源獲取(數據源、配置、緩存預熱)。
  3. 服務 service(req, resp):每次請求由容器分配線程調用,默認分發到 doGet/doPost 等 doXxx 方法。
  4. 銷毀 destroy():容器關閉或應用重新部署前調用一次,釋放資源(關閉連接池、線程池、寫持久化)。
  5. 協載:容器從內存移除 Servlet 類,實例變為不可達等待 GC。

web.xml 配置

<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>

核心 Servlet 代碼(帶時間戳日誌)

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("

Servlet 生命週期演示 (POST)

"); out.println(""); } @Override public void destroy() { log("[" + df.format(new Date()) + "] destroy()"); } }

運行效果演示

1. 容器啟動(load-on-startup=1)

[10:00:00.123] 構造器
[10:00:00.125] init()

2. 瀏覽器首次 GET 請求

GET 頁面

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

3. 表單提交 POST 請求

POST 頁面

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

4. 容器關閉/重新部署

[10:05:00.000] destroy()

控制台完整日誌

關鍵知識點總結

特性說明
實例唯一性全應用僅一個 Servlet 實例,多線程共享實例變量 → 線程不安全
init 執行次數僅 1 次(構造器後、首次 service 前)
service 執行次數每請求 1 次,由容器線程池並發調用
destroy 執行時機容器正常關閉、應用重新部署、Servlet 移除
構造器 vs init構造器無 ServletConfig,init 可獲取初始化參數、ServletContext
線程安全不要在 Servlet 實例變量保存請求狀態,用局部變量、ThreadLocal、請求域

常見面試追問

  1. Servlet 不是線程安全的,如何解決? 無實例變量、同步塊、ThreadLocal、請求域對象、無狀態設計。
  2. init 方法拋異常會怎樣? 容器標記 Servlet 不可用,後續請求直接 500,destroy 不會被調用。
  3. 能否在構造器裡用 ServletContext? 不能,構造器執行時 ServletConfig/Context 尚未注入,必須在 init 中使用。
  4. SingleThreadModel 已棄用,為什麼? 容器為每請求創建實例/同步隊列,性能極差,無法解決共享資源(Session、靜態變量)競爭。

徹底理解生命週期與多線程模型,是編寫高性能、穩定 Servlet 應用的前提。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved