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

JSP 生命週期完整解析

     編輯:JSP 入門

JSP 生命週期完整解析

JSP 本質是 Servlet。容器將 .jsp 翻譯為 _jspService() 方法的 Servlet 類別,生命週期遵循 Servlet 規格,增加翻譯/編譯階段。


四大階段

  1. 翻譯與編譯:.jsp → .java → .class
  2. 初始化:載入類別、實例化、呼叫 jspInit()
  3. 請求處理:每次請求呼叫 _jspService(req, resp)
  4. 銷毀:呼叫 jspDestroy()、卸載類別

階段一:翻譯與編譯

觸發時機

  • 首次請求 JSP
  • JSP 檔案修改後 (時間戳更新)
  • 容器啟動時預編譯 (配置 precompilation)

過程

  1. 解析 JSP 模板文字、指令、動作、Scriptlet、EL
  2. 生成 Servlet 原始碼 (_jsp.java)
  3. 編譯為位元組碼 (_jsp.class)

生成的 Servlet 結構

public class MyJsp extends HttpJspBase {
  // 1. 靜態初始化區塊 (JSP 宣告 -> 欄位/方法)
  private static final String CONST = "value";
  public void jspInit() { ... }
  public void jspDestroy() { ... }

  // 2. _jspService 處理每個請求
  public void _jspService(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    // 隱含物件初始化
    PageContext pageContext = ...;
    HttpSession session = ...;
    ServletContext application = ...;
    JspWriter out = ...;
    // 模板文字 -> out.write("...")
    // Scriptlet -> 直接插入 Java 程式碼
    // Expression -> out.print(...)
    // 動作元素 -> 對應 API 呼叫
  }
}

階段二:初始化

  • 容器載入 Servlet 類別
  • 建立單一實例 (預設單例模式,isThreadSafe=true)
  • 呼叫 jspInit() — 只執行一次

自訂初始化

<%! 
  private DataSource dataSource;
  public void jspInit() {
    // 從 JNDI 取得連線池
    try {
      Context ctx = new InitialContext();
      dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
    } catch (NamingException e) {
      throw new RuntimeException(e);
    }
  }
  
  public void jspDestroy() {
    // 清理資源 (DataSource 通常由容器管理,此處僅示意)
  }
%>

階段三:請求處理 (_jspService)

  • 每個 HTTP 請求對應一次呼叫
  • 多執行緒並發執行 (同一實例)
  • 參數:HttpServletRequest, HttpServletResponse
  • 處理所有 HTTP 方法:GET, POST, PUT, DELETE 等

隱含物件在 _jspService 中的初始化順序

PageContext pageContext = JspFactory.getDefaultFactory().getPageContext(
    this, request, response, null, true, 8192, true);
HttpSession session = pageContext.getSession();
ServletContext application = pageContext.getServletContext();
ServletConfig config = pageContext.getServletConfig();
JspWriter out = pageContext.getOut();
// page = this
// exception = null (除非是錯誤頁面)

階段四:銷毀

  • 容器關閉、應用重新部署、JSP 移除時觸發
  • 呼叫 jspDestroy() — 只執行一次
  • 釋放資源:關閉連線、線程池、暫存檔案
  • Servlet 實例失效,等待 GC

完整生命週期示範程式

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%! 
  private int initCount = 0;
  private int serviceCount = 0;
  private int destroyCount = 0;
  
  public void jspInit() {
    initCount++;
    System.out.println("jspInit() 執行,第 " + initCount + " 次初始化");
  }
  
  public void jspDestroy() {
    destroyCount++;
    System.out.println("jspDestroy() 執行,第 " + destroyCount + " 次銷毀");
  }
%>
<%
  serviceCount++;
  System.out.println("_jspService() 執行,第 " + serviceCount + " 次請求處理");
%>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>JSP 生命週期示範</title></head>
<body>
  <h2>JSP 生命週期計數器</h2>
  <table border="1">
    <tr><th>階段</th><th>次數</th></tr>
    <tr><td>初始化 (jspInit)</td><td><%= initCount %></td></tr>
    <tr><td>請求處理 (_jspService)</td><td><%= serviceCount %></td></tr>
    <tr><td>銷毀 (jspDestroy)</td><td><%= destroyCount %></td></tr>
  </table>
  <p>重新整理頁面觀察 serviceCount 增加,重新部署觀察 init/destroy 變化。</p>
</body></html>

關鍵特性總結

特性說明
單例多執行緒一個 JSP 類別對應一個實例,並發請求共享,需注意執行緒安全
翻譯延遲首次請求較慢 (編譯),後續請求快 (直接呼叫 _jspService)
自動重載修改 .jsp 檔案後,容器偵測時間戳變化自動重新翻譯編譯
預編譯生產環境建議建置時預編譯 (Maven jspc plugin),避免首次請求延遲
錯誤頁面isErrorPage="true" 的 JSP 會在 _jspService 捕獲例外並設定 exception 隱含物件

與 Servlet 生命週期對照

JSPServlet備註
翻譯/編譯編譯 .javaJSP 多一步翻譯
jspInit()init(ServletConfig)JSP 隱含 ServletConfig
_jspService(req,resp)service(req,resp)JSP 固定方法名
jspDestroy()destroy()對應

最佳實踐

  1. 生產環境啟用預編譯:Maven <plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId>...</plugin> 或 Ant jspc。
  2. 避免在 jspInit/jspDestroy 做耗時操作:影響啟動/關閉速度,改用 ServletContextListener。
  3. 靜態資源分離:CSS/JS/圖片勿放在 JSP 內,由 Nginx/CDN 直接服務。
  4. 監控編譯錯誤:CI/CD 階段驗證 JSP 編譯通過。
  5. 理解單例模式:請勿在 Declaration 定義非最終欄位,改用範圍屬性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved