前言
Struts2 的值棧是框架核心數據存儲與訪問機制,支撐標簽庫、OGNL 表達式、數據綁定。本文深度解析值棧的雙重結構、ActionContext 獲取方式、s:property 訪問語法、Action 實例自動入棧等關鍵知識點。
一、值棧獲取入口
// 1. 從 ActionContext 獲取(Action 中)
ValueStack valueStack = ActionContext.getContext().getValueStack();
// 2. 創建業務對象並入棧
User user = new User();
user.setUsername("zhangsan");
user.setPassword("123456");
// 3. 壓入棧頂
valueStack.push(user);
二、值棧雙重邏輯結構
| 結構 | 實現類 | 存儲內容 | 訪問方式 |
| 對象棧 | CompoundRoot (ArrayList 實現) | Action 實例、業務模型對象、臨時對象 | 索引 [0] 棧頂,[1] 次頂... 支持屬性鏈 [0].username |
| Map 棧 | OgnlContext (Map 實現) | request/session/application/parameters/attr 等 Map 引用 | 鍵名直接訪問 #request.key、#session.user |
結構圖解
ValueStack (CompoundRoot + OgnlContext)
├── CompoundRoot [Object Stack] ← push/pop 操作對象
│ ├── [0] Action 實例 (棧頂,自動入棧)
│ ├── [1] 業務 VO
│ └── [n] 臨時對象
└── OgnlContext [Map Stack / Context Map] ← 只讀上下文引用
├── "request" → HttpServletRequest Map
├── "session" → HttpSession Map
├── "application" → ServletContext Map
├── "parameters" → RequestParameter Map
├── "attr" → 頁面/請求/會話/應用統一搜索
└── ...
三、OGNL 表達式訪問語法大全
| 目標 | OGNL 表達式 | 說明 |
| 棧頂對象屬性 | username 或 [0].username | 省略索引默認從棧頂搜索 |
| 指定索引對象屬性 | [1].address.city | 從第 2 個對象開始搜索 |
| Map 棧 request 屬性 | #request.msg | # 標識 Map 棧上下文 |
| session 屬性 | #session.user.id | |
| application 屬性 | #application.config.version | |
| 請求參數 | #parameters.id[0] | 參數為數組,取第一個 |
| attr 統一搜索 | #attr.key | 按 page→request→session→application 順序 |
| 調用方法 | user.getFullName() | 支持無參方法調用 |
| 集合操作 | users.{name} | 投影:提取所有用戶名組成新 List |
| 過濾 | users.{? age > 18} | 篩選成年用戶 |
四、s:property 標簽實戰
<-- 訪問棧頂 Action 的 message 屬性 -->
<s:property value="message" />
<-- 顯式指定棧頂 -->
<s:property value="[0].message" />
<-- 訪問第 2 個對象的屬性 -->
<s:property value="[1].detail.info" />
<-- 訪問 request 域 -->
<s:property value="#request.errorMsg" />
<-- 訪問請求參數 -->
<s:property value="#parameters.keyword[0]" />
五、Action 實例自動入棧機制
- Struts2 攔截器鏈中
ServletConfigInterceptor / ModelDrivenInterceptor 負責
- 執行 Action 方法前,自動
valueStack.push(actionInstance)
- 因此 Action 的屬性、Getter 可直接在 JSP 中用
propertyName 訪問
- ModelDriven 模式下,
getModel() 返回的模型對象也會被壓入棧頂(在 Action 實例之上)
六、常見坑點與避坑
| 坑點 | 現象 | 解決 |
| 同名屬性遮蔽 | 棧頂對象有 username,次頂也有,取值不符預期 | 顯式指定索引 [1].username 或重命名字段 |
| Map 棧鍵衝突 | request 有 key="user",session 也有,#attr.user 取哪個? | 按 page→request→session→application 優先級,或顯式 #request.user |
| OGNL 表達式注入 | 用戶輸入被直接拼接進表達式執行 | 嚴禁拼接用戶輸入,使用參數化標簽屬性 |
| 值棧對象未清理 | 長流程 Action 鏈導致棧內對象積累,內存洩漏 | 用完即 valueStack.pop(),或縮短 Action 鏈 |
七、總結
- 值棧 = 對象棧 + Map 棧,前者存業務對象,後者存 Web 域引用
push/pop 操作對象棧,[n].property 訪問對象棧,#context.key 訪問 Map 棧
- Action 實例自動入棧頂,ModelDriven 模型優先入棧
- OGNL 支持投影、過濾、方法調用等強大集合操作
- 生產環境注意同名遮蔽、表達式注入、內存洩漏風險