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

Servlet 技術演進與生命週期深度解析:從 CGI 到多線程模型

     編輯:Java 綜合教學

Servlet 技術演進與生命週期深度解析

Servlet 是 Java Web 開發的基石。理解其誕生背景、核心特點與生命週期,有助於我們寫出高效、穩健的伺服器端程式。

一、伺服器端技術演進史:為什麼需要 Servlet?

1. CGI 時代(Common Gateway Interface)

  • 模式:每個請求啟動一個獨立進程。
  • 痛點:
    • 進程建立/銷毀開銷極大,併發能力極弱。
    • 進程間隔離,無法共享記憶體資源(如資料庫連接池、快取)。
    • 開發語言不限,但易受作業系統進程模型影響,錯誤難以排查。

2. 進程池技術

  • 預先啟動一組進程,請求到來時分配空閒進程。
  • 緩解了頻繁建立進程的開銷,但本質仍是重量級進程,資源消耗大,擴展性受限。

3. Servlet 技術誕生

  • 核心突破:基於多線程模型,單一 JVM 進程處理所有請求。
  • 優勢:
    • 線程輕量,建立/切換成本低,支撐高併發。
    • 同一進程內線程共享堆記憶體,天然支援資源共享(連接池、Session、Application 級快取)。
    • Java 語言特性帶來跨平台、記憶體安全、豐富的生態庫。

二、Servlet 核心特點

  1. 容器管理:Servlet 由 Web 容器(Tomcat、Jetty 等)實例化、初始化、銷毀,開發者不直接 `new Servlet()`。
  2. 請求驅動:容器接收 HTTP 請求,封裝為 `HttpServletRequest`/`Response`,調用 Servlet 的 `service()` 方法。
  3. 非 JavaBean:生命週期受容器控制,擁有 `init()`、`destroy()` 等回調方法。
  4. 單實例多線程:預設情況下,容器為每個 Servlet 類只建立一個實例,所有請求共享該實例,透過線程隔離請求數據。

三、Servlet 生命週期完整流程

public interface Servlet {
    // 1. 初始化:容器建立實例後,僅呼叫一次
    void init(ServletConfig config) throws ServletException;

    // 2. 服務:每個請求一條線程呼叫,核心業務邏輯入口
    void service(ServletRequest req, ServletResponse res) 
        throws ServletException, IOException;

    // 3. 銷毀:容器卸載 Servlet 前呼叫,僅一次,釋放資源
    void destroy();

    // 輔助方法
    ServletConfig getServletConfig();
    String getServletInfo();
}

生命週期階段詳解

  • 載入類檔案
  • 透過無參建構子建立實例
  • 讀取初始化參數 (`ServletConfig`)
  • 建立昂貴資源:資料庫連接池、快取、線程池
  • 解析請求、執行業務邏輯、生成回應
  • HttpServlet 會分派至 doGet/doPost 等
  • 釋放資源:關閉連接池、儲存狀態、停止排程任務
  • 階段觸發時機執行次數關鍵動作線程安全
    載入與實例化 容器啟動時(配置 `load-on-startup`)或首次請求到達 1 次 單線程
    初始化 (init) 實例化後立即 1 次 單線程
    請求處理 (service) 每個 HTTP 請求到達 N 次 多線程併發,需注意共享變數安全
    銷毀 容器關閉、專案重新部署、Servlet 移除 1 次 單線程(等待所有 service 線程結束)

    四、HttpServlet 請求分派機制

    `HttpServlet` 覆寫了 `service()`,根據 HTTP 方法分派:

    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals("GET")) {
            doGet(req, resp);
        } else if (method.equals("POST")) {
            doPost(req, resp);
        } // ... PUT, DELETE, HEAD, OPTIONS, TRACE
    }

    開發者通常只需覆寫 `doGet`/`doPost` 等具體方法。

    五、線程安全實戰原則

    • 避免實例變數存儲請求狀態:請求參數、業務中間結果請放在方法區域變數(棧上),天然線程安全。
    • 共享資源加鎖或使用線程安全類:如 `AtomicInteger`、`ConcurrentHashMap`、`ThreadLocal`。
    • 唯讀實例變數安全:`final` 修飾的不可變物件、初始化後不再修改的配置資訊。
    • 慎用 `SingleThreadModel`:已棄用,效能極差,不解決 Session/Application 層面共享問題。

    六、最佳實踐建議

    1. 繼承 `HttpServlet` 而非直接實作 `Servlet` 介面。
    2. 在 `init()` 中完成昂貴資源初始化,`destroy()` 中確實釋放。
    3. 業務邏輯委託給 Service 層,Servlet 僅負責協議轉換與流程控制。
    4. 善用 `ServletContextListener` 管理應用級資源(比 Servlet 生命週期更宏觀)。
    5. 配置 `async-supported="true"` 支援 Servlet 3.0+ 非同步處理,提升長連接/耗時操作吞吐量。

    Servlet 雖是「老」技術,但 Spring MVC、Struts 等主流框架底層皆建立於此。吃透其生命週期與線程模型,是進階 Java Web 工程師的必經之路。

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