0%

OGNL

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 结构,存储applicationsession等上下文对象,与 OGNL 的上下文对应。

图示简化结构

1
2
3
4
5
6
7
8
9
10
11
ValueStack
├─ Object Stack(栈顶→栈底)
│ ├─ 当前Action实例(如UserAction)
│ ├─ 模型对象(如User)
│ └─ 其他辅助对象
└─ Context Map
├─ application: { ... }
├─ session: { ... }
├─ request: { ... }
├─ parameters: { ... }
└─ attr: { ... }

核心特性

  • 作为 OGNL 的根对象:访问对象栈中的属性时无需前缀,OGNL 会从栈顶向栈底依次查找匹配的属性。例如,若栈顶是UserAction,其中包含user属性,则user.name会直接访问该属性;
  • 自动压入对象:
    • Action 实例被创建后会自动压入对象栈;
    • 通过modelDriven拦截器(实现ModelDriven接口)可将模型对象压入栈顶,优先于 Action 实例被访问;
  • 数据共享:Action 处理过程中产生的数据(如查询结果)可存入值栈,供视图(JSP)通过 OGNL 访问,实现 Action 与视图的解耦。

操作值栈的常用方法

在 Action 中可通过ActionContext获取 ValueStack 并操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UserAction extends ActionSupport {
public String execute() {
// 获取ValueStack对象
ValueStack valueStack = ActionContext.getContext().getValueStack();

// 压入对象(会成为栈顶)
User user = new User("张三", 20);
valueStack.push(user);

// 设置属性(会存入栈顶对象中,若栈顶是User,则等价于user.setAge(21))
valueStack.set("age", 21);

// 查找属性(从栈顶向栈底查找)
Object name = valueStack.findValue("name"); // 结果为"张三"

return SUCCESS;
}
}

OGNL 与值栈的协同工作机制

  1. 请求处理流程中的数据流转

    • 客户端发送请求,Struts2 创建 Action 实例并将其压入值栈;
    • Action 的业务方法执行时,将处理结果(如查询到的列表、用户对象)存入值栈;
    • 视图(如 JSP)通过 OGNL 表达式访问值栈中的数据(如<s:property value="user.name"/>);
    • OGNL 解析表达式时,以值栈为根对象,从栈顶向栈底查找匹配的属性,返回对应的值。
  2. 示例:在 JSP 中使用 OGNL 访问值栈数据
    若 Action 中压入了User对象(包含nameage属性),则 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()"/>

关键注意事项

  1. 值栈的访问优先级
    OGNL 会从栈顶开始查找属性,若栈中存在多个对象包含同名属性,栈顶对象的属性会被优先访问。例如,若 Action 和模型对象都有name属性,且模型对象在栈顶,则value="name"会返回模型对象的name

  2. ModelDriven接口的影响
    实现ModelDriven<T>接口的 Action 会将模型对象(getModel()返回的对象)压入栈顶,此时访问属性会直接操作模型对象,无需通过 Action 的 getter 方法(如value="name"等价于model.getName(),而非action.getModel().getName())。

  3. 避免在值栈中存储大量数据
    值栈在请求处理完成后会被销毁,但若存储大量数据(如大集合),可能导致内存占用过高,影响性能。

  4. 静态方法调用的配置
    默认情况下,Struts2 禁止访问静态方法,需在struts.xml中开启:

    1
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"/>

总结

OGNL 与值栈是 Struts2 中数据传递的核心机制:

  • OGNL提供了灵活的表达式语法,支持复杂的数据访问与操作;
  • 值栈(ValueStack) 作为 OGNL 的根对象,集中管理 Action 生命周期中的数据,是 Action 与视图之间数据传递的桥梁

欢迎关注我的其它发布渠道