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

JSP 聲明語法 <%! %> 深度解析:應用級共享變量陷阱

     編輯:關於JSP

兩種聲明語法對比

語法名稱生成位置作用域線程安全
<% int count = 0; %>Scriptlet (腳本片段)_jspService() 方法內部請求級 (每次請求重新初始化)安全 (局部變量)
<%! int count = 0; %>Declaration (聲明)Servlet 類成員變量位置**實例級** (同一 JSP 對應的 Servlet 實例共享)**不安全** (多線程共享實例變量)

實驗驗證

測試代碼 (test1.jsp, test2.jsp)

<%! int intCount = 0; %>
<%
    intCount++;
    out.print("第 " + intCount + " 人次進入本頁面!");
%>

測試環境

JRun 3 + JDK 1.2 (經典環境,現代容器行為一致)

測試結果

  • 同一頁面多用戶訪問:計數器遞增,**所有用戶共享同一變量**,初始化 intCount=0 僅在類加載時執行一次(首次請求)。
  • 不同頁面互不影響:test1.jsp 與 test2.jsp 編譯為不同 Servlet 類,各自擁有獨立的 intCount 成員變量。

原理深度剖析

JSP 編譯為 Servlet 類結構:

public class test1_jsp extends HttpJspBase {
    // <%! ... %> 生成在此處
    int intCount = 0;

    public void _jspService(HttpServletRequest request, HttpServletResponse response) {
        // <% ... %> 生成在此處
        intCount++;
        out.print("第 " + intCount + " 人次進入本頁面!");
    }
}
  • 容器啟動時加載類,**單例模式**創建一個實例服務所有請求。
  • 成員變量 intCount 屬於該實例,**所有請求線程共享**。
  • 無同步機制 → 競態條件 → 計數錯誤、數據競爭。

等效於 ASP 的 Application 對象

行為類似 ASP 的 Application("count"):全局共享、進程生命週期內持久。但 JSP 聲明**不支持集群**,多 JVM 實例各自維護副本。

危害與誤區

  1. 計數器錯誤:併發下 count++ 非原子操作,導致漏計/重計。
  2. 業務數據洩漏:若聲明存放用戶對象 <%! User currentUser; %>,多用戶將互相覆蓋,嚴重安全事故。
  3. 內存洩漏:聲明持有大對象且無釋放機制,隨應用生命週期常駐內存。
  4. 測試難發現:單用戶測試正常,壓測/生產才暴露。

正確替代方案

現代容器行為差異

  • Tomcat 9+ 默認開發模式下修改 JSP 會觸發重編譯、類重載,**聲明變量會重置**,掩蓋了共享問題,誤導開發者以為安全。
  • 生產模式 (development="false") 或預編譯部署時,類不會熱重載,共享問題真實存在。

總結

嚴禁在 JSP 中使用 `<%! %>` 聲明實例變量存儲可變狀態。 它是 Servlet 單例模式下的共享實例字段,極易導致併發Bug與安全漏洞。請根據數據生命週期選擇正確的範圍對象 (page/request/session/application),並配合線程安全手段 (AtomicInteger, Collections.synchronizedMap, ThreadLocal 等)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
需求正確範圍實現方式
請求級計數/數據page / request<% int count = 0; %> 或 request.setAttribute()
用戶會話級數據sessionsession.setAttribute("count", val)
全局應用級計數/配置applicationapplication.setAttribute("count", val) + 同步/原子類
全局單例服務/DAOapplicationSpring Bean / ServletContextListener 初始化 / 靜態工具類