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

Struts2 ValueStack (值棧) 深度解析:OGNL 表達式、作用域鏈與最佳實踐

     編輯:關於C++

ValueStack 是 Struts2 數據傳遞與視圖渲染的核心組件。它不僅是一個對象棧,更結合 OGNL 表達式語言實現了強大的上下文查找機制。本文深入剖析其內部結構、對象存取規則、OGNL 在 JSP/Action/攔截器中的交互細節,並給出常見誤區與最佳實踐。

一、 核心概念:什麼是 ValueStack?

  • 物理結構:ArrayList<Object>,棧頂索引 0,push/pop 對應 add(0)/remove(0)。
  • 邏輯角色:請求級線程局部存儲,承載 Action 實例、Model 對象、臨時變量、標簽迭代上下文等。
  • 默認實現:OgnlValueStack,集成 OGNL 引擎解析表達式。

二、 對象入棧機制與順序

階段入棧對象來源位置
1. Action 創建後Action 實例框架自動棧底 (最早)
2. ModelDriven 攔截器getModel() 返回對象ModelDrivenInterceptorAction 之上
3. 自定義攔截器任意對象stack.push(obj)當前棧頂
4. JSP 標簽迭代變量、<s:push> 值<s:iterator>, <s:push>標簽體內臨時,結束自動 pop

關鍵點:同一請求中,Action 永遠在棧底;越晚入棧的對象優先級越高(OGNL 查找順序)。

三、 OGNL 表達式在 ValueStack 上的查找算法

對於表達式 property,ValueStack 依次檢查棧中對象(從頂向底):

  1. 若對象為 Map,嘗試 map.get("property")。
  2. 若對象為 JavaBean,嘗試 getProperty() / isProperty()。
  3. 若對象為數組/List,嘗試索引訪問(如 [0])。
  4. 找到非 null 即返回,否則繼續下一個對象。
  5. 全棧查找無果,拋出 OgnlException 或返回 null (視標簽屬性)。

繼承鏈查找:OGNL 會沿著對象繼承鏈查找 getter,直到 Object。

四、 進階訪問語法:精準控制查找範圍

語法含義示例
property從棧頂開始查找 (等價 [0].property)<s:property value="name"/>
[n].property從第 n 層 (0-based) 開始查找,跳過上層<s:property value="[1].name"/> 訪問 Action 的 name
top.property訪問棧頂對象本身的屬性 (同 [0].property)<s:property value="top.name"/>
[n].top.property訪問第 n 層對象本身<s:property value="[1].top.name"/>
#attr.property訪問 Page/Request/Session/Application 作用域屬性<s:property value="#request.msg"/>
%{expr}強制標簽屬性按 OGNL 解析 (非字符串字面量)<s:set var="x" value="%{1+2}"/>

五、 典型使用場景與代碼示例

1. Action 中訪問/操作 ValueStack

public class MyAction extends ActionSupport {
    public String execute() {
        ValueStack stack = ActionContext.getContext().getValueStack();
        // 存入臨時對象,供 JSP 使用
        stack.push(new User("temp", 99));
        // 設置值 (作用於棧頂對象,若無 setter 則創建 Map 存儲)
        stack.setValue("tempKey", "tempValue");
        // 獲取值
        String val = (String) stack.findValue("tempKey");
        return SUCCESS;
    }
}

2. 自定義攔截器操作 ValueStack

public class MyInterceptor extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        ValueStack stack = invocation.getStack();
        // 在 Action 執行前修改參數
        stack.setValue("preProcessed", true);
        return invocation.invoke();
    }
}

3. JSP 中迭代與上下文切換

<!-- iterator 將當前元素 push 到棧頂 -->
<s:iterator value="userList" var="u" status="st">
    <s:property value="name"/> <!-- 直接訪問 User.name -->
    <s:property value="[1].actionProp"/> <!-- 訪問 Action (棧第2層) 的屬性 -->
    <s:push value="address">
        <s:property value="city"/> <!-- 訪問 Address.city -->
    </s:push>
</s:iterator>

六、 常見誤區與排查指南

  1. 屬性被覆蓋/讀不到:棧中多個對象有同名屬性,優先返回棧頂對象的值。解決:用 [n].prop 指定層級,或重命名屬性。
  2. <s:property value="obj.prop"/> 不報錯但無輸出:OGNL 查找失敗返回 null,標簽默認不拋異常。排查:檢查 getter 名稱、對象是否入棧、類型轉換錯誤 (OGNL 日誌開啟 DEBUG)。
  3. ModelDriven 導致 Action 屬性不可訪問:Model 入棧在 Action 之上,同名屬性屏蔽 Action。建議:Model 僅包含表單數據,Action 業務屬性改名或用 [1].prop。
  4. 線程安全:ActionContext.getContext() 基於 ThreadLocal,單請求單線程安全;切勿 在單例 Bean/靜態字段中持有 ValueStack 引用跨請求使用。
  5. 大對象入棧導致內存壓力:ValueStack 隨請求銷毀,但請求處理期間對象常駐。避免 推入大集合/大文件流,改用請求屬性 #request.bigData 或流式輸出。

七、 內部原理速覽

  • OgnlValueStack 繼承 CompoundRoot (ArrayList) 實現 ValueStack 接口。
  • findValue(expr) 內部調用 Ognl.getValue(expr, root, context),root 即棧自身 (實現 Map 接口,get(key) 觸發上述查找算法)。
  • setValue(expr, value) 僅作用於棧頂對象 (或創建 CompoundRoot 內部 Map 存儲)。
  • 請求結束時,ActionContextCleanUp 監聽器清理 ThreadLocal,棧對象可被 GC。

ValueStack + OGNL 是 Struts2 「約定優於配置」的基石:Action 屬性自動入棧,JSP 直接用屬性名訪問,無需繁瑣的 request.setAttribute。掌握 棧順序、查找算法、層級選擇語法,即可駕馭 90% 的數據顯示場景,避免「為什麼取不到值」的困擾。

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