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

Servlet捕獲JSP輸出生成靜態HTML:ResponseWrapper實現頁面靜態化

     編輯:關於JSP

將動態JSP渲染結果捕獲並寫入靜態.html文件,後續請求直接訪問靜態文件,繞過JSP引擎,大幅提升高頻頁面響應速度。本文演示基於ServletOutputStream與HttpServletResponseWrapper的輸出攔截全鏈路。

核心原理

  1. Servlet接收請求,創建ByteArrayOutputStream作為內存緩衝區。
  2. 自定義ServletOutputStream子類,write()委託給ByteArrayOutputStream。
  3. 自定義HttpServletResponseWrapper,重寫getOutputStream()、getWriter()返回上述包裝流。
  4. 使用RequestDispatcher.include()包含目標JSP,輸出被攔截存入內存。
  5. 內存數據寫入物理.html文件,重定向到靜態文件。

完整Servlet代碼(ToHtmlServlet)

package examples;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ToHtml extends HttpServlet {
    private static final String CONTENT_TYPE = "text/html; charset=GBK";

    public void init() throws ServletException {}

    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType(CONTENT_TYPE);
        service(req, resp);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext sc = getServletContext();
        String jspUrl = "/index.jsp";           // 目標動態頁面
        String htmlFile = "index.htm";          // 生成的靜態文件名
        String htmlPath = "e:\\Tomcat 5.5\\webapps\\jspTohtml\\index.htm"; // 物理路徑

        RequestDispatcher rd = sc.getRequestDispatcher(jspUrl);

        // 1. 內存字節緩衝區
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // 2. 自定義ServletOutputStream委託給BAOS
        final ServletOutputStream sos = new ServletOutputStream() {
            public void write(byte[] data, int offset, int length) { baos.write(data, offset, length); }
            public void write(int b) throws IOException { baos.write(b); }
        };

        // 3. 字符流包裝(JSP可能用out.print字符輸出)
        final PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos));

        // 4. 包裝Response,攔截輸出流
        HttpServletResponse wrappedResp = new HttpServletResponseWrapper(resp) {
            public ServletOutputStream getOutputStream() { return sos; }
            public PrintWriter getWriter() { return pw; }
        };

        // 5. 包含JSP執行,輸出被捕獲到baos
        rd.include(req, wrappedResp);
        pw.flush();

        // 6. 寫入靜態文件
        try (FileOutputStream fos = new FileOutputStream(htmlPath)) {
            baos.writeTo(fos);
        }

        // 7. 重定向到靜態頁面
        resp.sendRedirect(htmlFile);
    }

    public void destroy() {}
}

web.xml配置

<servlet>
    <servlet-name>ToHtml</servlet-name>
    <servlet-class>examples.ToHtml</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ToHtml</servlet-name>
    <url-pattern>/ToHtml</url-pattern>
</servlet-mapping>

測試用JSP(index.jsp)

<%@ page contentType="text/html; charset=gb2312" %>
<html><head><title>Cache Filter Test</title></head><body>
簡單測試: s=
<%
    int s = 0;
    // 模擬耗時計算
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 1000; j++) { s += j; }
    }
    out.print(s);
%>
</body></html>

效果驗證

  1. 訪問https://localhost:8080/jspTohtml/ToHtml。
  2. Servlet執行index.jsp(含耗時循環),捕獲輸出寫入index.htm。
  3. 重定向到index.htm,瀏覽器顯示計算結果。
  4. 再次訪問index.htm直接由默認Servlet靜態服務,無JSP執行開銷。

生成靜態頁面效果圖

關鍵技術細節

  • 雙流攔截:同時包裝getOutputStream()(二進制)與getWriter()(字符),兼容JSPout.print()與response.getOutputStream()混用場景。
  • include而非forward:include()返回後繼續執行,可獲取捕獲內容;forward()會提交響應不可攔截。
  • 編碼一致性:OutputStreamWriter(baos)默認用平台編碼,生產環境顯式指定new OutputStreamWriter(baos, "GBK")與JSPpageEncoding一致。
  • 併發安全:每請求創建獨立ByteArrayOutputStream,無共享狀態。

現代化替代方案

方案優勢適用場景
Nginx fastcgi_cache / proxy_cache基礎設施層透明緩存,應用零代碼全站通用、高併發靜態化
Spring @Cacheable + 模板引擎方法級緩存,Key可SpEL表達式業務邏輯緩存、片段緩存
Thymeleaf/FreeMarker TemplateEngine.process() 直寫文件無需Servlet容器,構建期/定時任務生成構建時靜態化、部署包含靜態文件
SSG (Next.js/Astro/Nuxt)構建期預渲染所有頁面內容型站點、極致性能

Servlet捕獲JSP輸出生成靜態文件是經典「偽靜態化」模式。理解ResponseWrapper攔截機制,對排查現代框架中響應體獲取異常、緩存不生效等問題極有幫助。

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