Struts2 中的 OGNL 与值栈(ValueStack)详解
OGNL(Object-Graph Navigation Language,对象图导航语言)是 Struts2 框架默认使用的表达式语言,主要用于在视图(如 JSP)中访问 Action 中的数据,而值栈(ValueStack)则是 OGNL 的核心操作对象,贯穿整个 Action 的生命周期。下面详细解析两者的工作机制与关联。
OGNL 概述
基本概念
OGNL 是一种功能强大的表达式语言,支持:
- 访问对象的属性(如
user.name
); - 调用对象的方法(如
list.size()
); - 操作集合(如
list[0]
); - 实现类型转换(如字符串转日期);
- 访问静态方法和属性(需通过
@类全名@方法名
语法)。
在 Struts2 中,OGNL 主要应用于:
- JSP 页面中通过
<s:property value="表达式"/>
获取数据; struts.xml
中配置动态参数(如<param name="id">${userId}</param>
);- 输入校验的表达式配置。
OGNL 的上下文环境
OGNL 的求值依赖于上下文(Context),在 Struts2 中,上下文由ActionContext
维护,包含以下核心对象:
- 值栈(ValueStack):作为 OGNL 的根对象(Root),访问其中的对象无需前缀;
application
:对应 Servlet 的ServletContext
;session
:对应 HttpSession;request
:对应 HttpServletRequest;parameters
:请求参数集合;attr
:按pageContext→request→session→application
顺序查找属性。
访问非根对象的语法:需通过#
前缀指定上下文对象,例如:
- 访问请求参数:
#parameters.name
; - 访问 Session 属性:
#session.user
; - 访问 Application 属性:
#application.config
。
值栈(ValueStack)详解
基本概念
- 定义:ValueStack 是 Struts2 框架在内存中维护的一个数据结构,本质上是一个栈结构,用于存储 Action 实例、相关对象(如模型对象)以及临时数据。
- 生命周期:与 Action 实例一一对应,从 Action 创建到响应结束,贯穿整个请求处理过程。
- 存储位置:Struts2 将 ValueStack 对象以属性名
struts.valueStack
存入 HttpServletRequest 中,可通过request.getAttribute("struts.valueStack")
获取。
结构组成
ValueStack 由两部分组成:
- 对象栈(Object Stack):一个栈结构,存储 Action 实例、模型对象(如
User
)等,遵循 “后进先出” 原则,新压入的对象会成为栈顶(优先被访问); - 上下文栈(Context Map):一个 Map 结构,存储
application
、session
等上下文对象,与 OGNL 的上下文对应。
图示简化结构:
1 | ValueStack |
核心特性
- 作为 OGNL 的根对象:访问对象栈中的属性时无需前缀,OGNL 会从栈顶向栈底依次查找匹配的属性。例如,若栈顶是
UserAction
,其中包含user
属性,则user.name
会直接访问该属性; - 自动压入对象:
- Action 实例被创建后会自动压入对象栈;
- 通过
modelDriven
拦截器(实现ModelDriven
接口)可将模型对象压入栈顶,优先于 Action 实例被访问;
- 数据共享:Action 处理过程中产生的数据(如查询结果)可存入值栈,供视图(JSP)通过 OGNL 访问,实现 Action 与视图的解耦。
操作值栈的常用方法
在 Action 中可通过ActionContext
获取 ValueStack 并操作:
1 | public class UserAction extends ActionSupport { |
OGNL 与值栈的协同工作机制
请求处理流程中的数据流转:
- 客户端发送请求,Struts2 创建 Action 实例并将其压入值栈;
- Action 的业务方法执行时,将处理结果(如查询到的列表、用户对象)存入值栈;
- 视图(如 JSP)通过 OGNL 表达式访问值栈中的数据(如
<s:property value="user.name"/>
); - OGNL 解析表达式时,以值栈为根对象,从栈顶向栈底查找匹配的属性,返回对应的值。
示例:在 JSP 中使用 OGNL 访问值栈数据
若 Action 中压入了User
对象(包含name
和age
属性),则 JSP 中可通过以下方式访问:1
2
3
4
5
6
7
8
9
10
11
12
13
14<!-- 访问栈顶User对象的name属性 -->
<s:property value="name"/> <!-- 等价于User.getName() -->
<!-- 访问栈顶User对象的age属性 -->
<s:property value="age"/>
<!-- 调用方法 -->
<s:property value="name.toUpperCase()"/> <!-- 将名字转为大写 -->
<!-- 访问集合元素 -->
<s:property value="users[0].name"/> <!-- 访问users集合的第一个元素的name -->
<!-- 访问静态方法(需在struts.xml中开启允许静态方法调用) -->
<s:property value="@java.lang.Math@random()"/>
关键注意事项
值栈的访问优先级:
OGNL 会从栈顶开始查找属性,若栈中存在多个对象包含同名属性,栈顶对象的属性会被优先访问。例如,若 Action 和模型对象都有name
属性,且模型对象在栈顶,则value="name"
会返回模型对象的name
。ModelDriven
接口的影响:
实现ModelDriven<T>
接口的 Action 会将模型对象(getModel()
返回的对象)压入栈顶,此时访问属性会直接操作模型对象,无需通过 Action 的 getter 方法(如value="name"
等价于model.getName()
,而非action.getModel().getName()
)。避免在值栈中存储大量数据:
值栈在请求处理完成后会被销毁,但若存储大量数据(如大集合),可能导致内存占用过高,影响性能。静态方法调用的配置:
默认情况下,Struts2 禁止访问静态方法,需在struts.xml
中开启:1
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
总结
OGNL 与值栈是 Struts2 中数据传递的核心机制:
- OGNL提供了灵活的表达式语法,支持复杂的数据访问与操作;
- 值栈(ValueStack) 作为 OGNL 的根对象,集中管理 Action 生命周期中的数据,是 Action 与视图之间数据传递的桥梁