valueStack(值栈)

时间:2023-03-09 08:51:31
valueStack(值栈)

值栈

值栈(ValueStack)就是 OGNL 表达式存取数据的地方。在一个值栈中,封装了一次请求所需要的所有数据。

在使用 Struts2 的项目中,Struts2 会为每个请求创建一个新的值栈,也就是说,值栈和请求是一一对应的关系,这种一一对应的关系使值栈能够线程安全地为每个请求提供公共的数据存取服务。

值栈的作用

值栈可以作为一个数据中转站在前台与后台之间传递数据,最常见的就是将 Struts2 的标签与 OGNL 表达式结合使用。

值栈实际上是一个接口,在 Struts2 中利用 OGNL 时,实际上使用的就是实现了该接口的 OgnlValueStack 类,这个类是 OGNL 的基础。

值栈的生命周期

值栈贯穿整个 Action 的生命周期,每个 Action 类的对象实例都拥有一个 ValueStack 对象,在 ValueStack 对象中保存了当前 Action 对象和其他相关对象。

Struts2 框架把 ValueStack 对象保存在一个名为 struts.valueStack 的 request 属性中,也就是说,值栈与 Action 的生命周期一致。值栈的生命周期随着 request 的创建而创建,随着 request 的销毁而销毁。

值栈的获取方式

要获取值栈中存储的数据,首先应该获取值栈。值栈的获取有两种方式:

● 在request中获取值栈

ValueStack 对象在 request 范围内的存储方式为 request.setAttribute("struts.valueStack",valuestack),可以通过如下方式从 request 中取出值栈的信息。

//获取 ValueStack 对象,通过 request 对象获取
ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

ServletActionContext.STRUTS_VALUESTACK_KEY 是 ServletActionContext 类中的常量,它的值为 struts.valueStack。

● 在ActionContext中获取值栈

在使用 Struts2 框架时,可以使用 OGNL 操作 Context 对象从 ValueStack 中存取数据,也就是说,可以从 Context 对象中获取 ValueStack 对象。实际上,Struts2 框架中的 Context 对象就是 ActionContext。

ActionContext 获取 ValueStack 对象的方式:

//通过 ActionContext 获取 valueStack 对象
ValueStack valueStack = ActionContext.getContext().getValueStack();

ActionContext 对象是在 StrutsPrepareAndExcuteFilter 的 doFilter() 方法中被创建的,在源码中用于创建 ActionContext 对象的 createActionContext() 方法内可以找到获取的 ValueStack 对象的信息。

方法中还有这样一段代码:

ctx=new ActionContext(stack.getContext());
//stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext

ValueStack 对象中的 Context 对象被作为参数传递给了 ActionContext 对象,这也就说明 ActionContext 对象中持有了 ValueStack 对象的引用,因此可以通过 ActionContext 对象获取 ValueStack 对象。

Struts2值栈内部结构详解

ValueStack 对象的内部有两个逻辑部分

● ObjectStack(root 栈):是 CompoundRoot 类型,用 ArrayList 定义,Struts2 把动作和相关对象压入 ObjectStack 中。

● ContextMap(Map 栈):是 OgnlContext 类型,是一个 Map 集合,Struts2 把各种各样的映射关系(一些 Map 类型的对象)压入 ContextMap 中。

通过运行项目查看 ValueStack 中的信息

首先,在 struts2Demo05 项目的 com.mengma.ognl 包中新创建一个名称为 ValueStackAction 的类,并在类中编写一个获取 ValueStack 对象的方法。

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackAction extends ActionSupport {
public String execute() throws Exception {
// 通过ActionContext获取valueStack对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
System.out.println(valueStack);
return SUCCESS;
}
}

然后,将新建的 Action 信息添加到 struts.xml 中。

<action name="valueStack" class="com.mengma.ognl.ValueStackAction">
<result name="success">index.jsp</result>
</action>

最后,在 ValueStackAction 类中的第 11 行处设置断点,以 Debug 模式启动项目。在浏览器的地址栏中输入地址 http://localhost:8080/struts2Demo05/valueStack.action后,MyEclipse 会进入 Debug 模式。从 Variables 窗口中可以看到 valueStack 的结构信息。

valueStack(值栈)

如图所示的 ValueStack 结构中,只需关注 context 对象和 root 对象。从图中可以看到,context 对象的类型为 OgnlContext,root 对象的类型为 CompoundRoot。如果要查看这两个类的源码,则可以看到如下语句:

OgnlContext extends Object implements Map
CompoundRoot extends ArrayList

从上述两个类的源码中可以看出,context 对象实际上就是一个 Map,root 对象实际上就是一个 ArrayList。也就说明了 ValueStack 的两个逻辑部分 ObjectStack 对应 ArrayList(root),ContextMap 对应 Map(context)。

映射关系:

一般情况下,root 对象在存储 Action 的相关信息时会把相关的映射压入 ContextMap 中,这些相关的映射具体如下。

  • parameters:该 Map 中包含当前请求的请求参数。
  • request:该 Map 中包含当前 request 对象中的所有属性。
  • session:该 Map 中包含当前 session 对象中的所有属性。
  • application:该 Map 中包含当前 application 对象中的所有属性。
  • attr:该 Map 按如下顺序检索某个属性:request,session,application。

需要注意的是,在 context 对象中包含了 root 对象,从 context 对象的结构中就可以查看到相关信息

valueStack(值栈)

从图中可以看到 context 对象中的 root 对象,这也就证明了 context 对象中包含了 root 对象,并且该对象的类型为 CompoundRoot。

Struts2获取值栈数据

在 Struts2 中,值栈的主要作用就是解决从 Action 到页面的数据交换问题。在采用属性驱动和模型驱动交换数据时,Struts2 会将对象自动存储到 ValueStack 中。

● 属性驱动:每次请求访问 Action 的对象时,Action 中的属性对象会被自动压入 ValueStack 中。

● 模型驱动:Action 如果实现了 ModelDriven 接口,那么 ModelDrivenInterceptor 拦截器会生效,会将 model 对象压入到 ValueStack 中。

属性对象或 model 对象存储到 ValueStack 中后,就可以直接从 ValueStack 中获取页面所需的数据。

属性驱动

1.创建实体类 User

创建一个名称为 User 的类,该类中包含 name 和 age 两个属性,以及其 getter 和 setter 方法。

public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

创建 Action

创建一个名称为 ValueStack2Action 的类

import com.mengma.domain.User;
import com.opensymphony.xwork2.ActionSupport;
public class ValueStack2Action extends ActionSupport {
private static final long serialVersionUID = -6671554902053439423L;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute() throws Exception {
return SUCCESS;
}
}

声明了一个 User 类型的属性 user 及其 getter 和 setter 方法,由于只进行存取值的操作,所以直接使用 Action 的 execute() 方法返回 SUCCESS 即可。

修改配置文件

在配置文件 struts.xml 的 <package> 元素中,增加 action 的配置信息

<action name="valueStack2" class="com.mengma.ognl.ValueStack2Action">
<result>/field.jsp</result>
</action>

创建页面文件

在项目的 WebContent 目录下创建一个名称为 field.jsp 的文件

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>属性驱动方式获取值栈中的数据</title>
</head>
<body>
<s:debug></s:debug>
姓名:<s:property value="user.name"/><br/>
年龄:<s:property value="user.age"/>
</body>
</html>

编写了一个 <s:debug> 标签,用于查看值栈的信息,然后分别使用 <s:property> 标签获取用户的姓名和年龄。

启动项目并查看结果

从请求地址中传入的参数值已经成功地在页面中显示出来,这就是使用属性驱动获取 ValueStack 中对象的方式;

valueStack(值栈)

User 对象传入 Action 对象时,会被自动压入 ValueStack 中,所以在 Action 的 execute() 方法中不存储值也能在页面取出。

此时单击 Debug 标签链接便可以查看到值栈中的参数传递情况:

valueStack(值栈)

在参数列表中可以到 Property Name 列表中的参数 user 就是页面中获取的对象,在 Property Value 中包含了 user 的属性值。

模型驱动

采用模型驱动的方式时,Struts2 会将 Action 中的 model 信息存入到值栈中。

创建 Action

创建一个名称为 ValueStack3Action 的类

import com.alin.mengma.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class ValueStack3Action extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public User getModel() {
return user;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute() throws Exception {
return SUCCESS;
}
}

创建了一个 User 类型的对象 user,实现了 ModelDriven 接口的 getModel() 方法,并在 getModel() 方法中返回 user 对象,这样就可以接收到和 user 对象中属性名称相同的参数的值。

修改配置文件

在配置文件 struts.xml 的 <package> 元素中,增加 action 的配置信息

<action name="valueStack3" class="com.mengma.ognl.ValueStack3Action">
<result>/model.jsp</result>
</action>

创建页面文件

创建一个名称为 model.jsp 的页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>模型驱动方式获取值栈中的数据</title>
</head>
<body>
<s:debug></s:debug>
姓名:<s:property value="name"/><br/>
年龄:<s:property value="age"/>
</body>
</html>

启动项目并查看结果

从请求中传入的参数 name 和 age 已经成功地在页面中显示出来。由于是模型驱动,所以不需要用 user.name 的形式传递参数;

valueStack(值栈)

接下来单击 Debug 链接查看 ValueStack 中存储的参数值:

从下图中可以看出,采用模型驱动的方式时,User 对象的信息被存储在值栈的栈顶。由于 Action 中提供了一个 getModel() 方法,所以使用模型驱动时可以从 Action 中获取 model 对象,而在传递参数时,直接用 name 和 age 即可,这就是采用模型驱动获取值栈中数据的方式。

valueStack(值栈)

通过EL访问值栈的数据

在 Struts2 框架中,使用 EL 表达式也可以直接访问值栈中的数据。

<hr/>
使用EL表达式访问值栈中的数据:<br/>
姓名:${model.name }<br/>
年龄:${model.age }

使用了 EL 表达式获取值栈中的数据,重新访问页面后

valueStack(值栈)

直接用 EL 表达式的方式同样取出了值栈中的数据。EL 表达式能够访问值栈中数据的原因,是因为 Struts2 框架对请求进行了增强,如果在 request 作用域中没有查找到对应的值,就会到值栈中查找。查找时,它会先被当作属性在 root 中查找,查找不到再被当作 key 在 contextMap 中查找对应的 value,查找到后会将数据取出。