文章目录
1 值栈是什么
简单的说:值栈
是对应每一个请求对象的轻量级的内存数据中心Struts2
中一个很激动人心的特性就是引入了值栈,在这里统一管理着数据,供Action
、Result
、Interceptor
等Struts2
的其他部分使用,这样一来,数据被集中管理起来而不会凌乱,大大方便了程序编写。Struts2
中关于值栈的另外一个很激动人心的特性就是:大多数情况下,根本无需关心值栈,你不用管它在哪里,不用管它里面有什么,你只需要去获取自己需要的数据就可以了。也就是说,你可以隐式的使用值栈。当然,如果编写自定义的Result
或拦截器等较复杂功能的时候,还是需要显示访问值栈的,因此,还是需要你掌握值栈的知识。
2 值栈能干什么
简单的说,值栈能够线程安全的为每个请求提供公共的数据存取服务。
当有请求到达的时候,Struts2
会为每个请求创建一个新的值栈,也就是说,值栈和请求是一一对应的,不同的请求,值栈也不一样,而值栈封装了一次请求所有需要操作的相关的数据。正是因为值栈和请求的对应关系,因而值栈能保证线程安全的为每个请求提供公共的数据存取服务。
3 值栈有什么?
事实上,到现在为止,我们一直在讲值栈
,这种说法其实是不够准确的。为什么呢?因为在Struts2
中,值栈又有广义和狭义之分:
3.1 狭义值栈
通常指的是实现com.opensymphony.xwork2.util.ValueStack
接口的对象,目前就是com.opensymphony.xwork2.ognl.OgnlValueStack
对象。
狭义值栈主要用来存取动态EL
(表达式语言)运算需要的值和结果,当然OgnlValueStack
对象主要是用来支持OGNL
(对象图导航语言)运算的。狭义值栈里面存放着一些OGNL
可以存取访问的数据,典型如:Action
的实例,这样就可以通过OGNL
来访问Action
实例中的属性的值了OGNL
表达式运算的值,可以设置到值栈中,可以主动访问值栈对象,强行设置OGNL
表达式产生的中间变量,比如在后面使用Struts2
的标签的时候,使用循环标签,自然会有循环的变量,这些都存放在值栈中
3.2 广义值栈
通常指的是ActionContext
对象,ActionContext
是Action
运行的上下文,每个ActionContext
是一个基本的容器,包含着Action
运行需要的数据,比如请求参数、会话等。
ActionContext
是线程安全的,每个线程有一个独立的ActionContext
,这样你就不用担心值栈中值的线程安全问题了。ActionContext
里面存放有很多的值,典型如:
-
Request
的parameters
:请求中的参数,要注意这里的数据是从请求对象里面拷贝出来的,因此这里数据的变化是不会影响到请求对象里面的参数的值的 -
Request
的Attribute
:请求中的属性,这里其实就是个Map
,存放着请求对象的属性数据,这些数据和请求对象的Attribute
是连动的 -
Session
的Attribute
:会话中的属性,这里其实就是个Map
,存放着会话对象的属性数据,这些数据和会话对象的Attribute
是连动的 -
Application
的Attribute
:应用中的属性,这里其实就是个Map
,存放着应用对象的属性数据,这些数据和应用对象的Attribute
是连动的 -
Value stack
:也就是狭义值栈,ActionContext
以value stack
作为被OGNL
访问的根,简单点说,OGNL
在没有特别指明的情况下,访问的就是value stack
里面的数据 -
attr
:在所有的属性范围中获取值,依次搜索page
、request
、session
和application
我们知道Xwork
与Web
是无关的,因此Action
不用去依赖于任何Web
容器,不用和Servlet
的API
去交互,但是Actio
n需要能访问到Web
应用的数据,不仅仅是取得请求参数的值,往往也需要在Action
里直接获取请求或会话的一些数据,对于这些数据,现在都可以通过ActionContext
来获取到。
3.3 关于广义和狭义
你会看到,在ActionContext
里面其实是包含着狭义值栈的,正是因为这个原因,再加上ActionContext
还包含其他的数据,因此把ActionContext
称为广义值栈。今后在说值栈的时候,没有特别指明的情况下,多数就是指的广义值栈,反正开发的时候都是说从值栈中获取值。当然,有一种情况除外,就是在页面上使用OGNL
的时候,没有特殊标识的情况下,默认是从value statck
中取值的。
4 值栈的基本使用
4.1 如何获取ActionContext
要获取ActionContext
有两个基本的方法,如果在不能获取到ActionInvocation
的地方,可以直接使用ActionContext
一个静态的getContext
方法,就可以访问到当前的ActionContext
了,示例如下:ActionContext ctx = ActionContext.getContext();
如果在能获取到ActionInvocation
的地方,比如在拦截器里面、自定义的Result
里面等,可以通过ActionInvocation
来获取到ActionContext,示例如下:
ActionContext ctx = actionInvocation.getInvocationContext();
4.2 获取过后,如何使用
ActionContext
主要的功能是用来存放数据的,典型的方法如下:
-
get(String key)
:根据key
从ActionContext
当前的存储空间里面获取相应的值 -
put(String key, Object value)
:把值存储在ActionContext
的存储空间里面 -
Map<String,Object> getApplication()
:返回ServletContext
中存储的值 -
Map<String,Object> getSession()
:返回HttpSession
中存储的值 -
Map<String,Object> getContextMap()
:返回当前context
存储的值 -
Map<String,Object> getParameters()
:返回HttpServletRequest
对象里面存储的,客户端提交的参数 -
ValueStack getValueStack()
:获取OGNL
的值栈
对于getXXX
的方法,都有对应的setXXX
方法,这里就不去赘述了,具体的请参看Struts2的API文档。
4.3 ValueStack的基本使用
ValueStack
被包含在ActionContext
中,ValueStack
也是用来存储对象的,但是它主要是通过OGNL
表达式来访问,也就是说,在Struts2
里面主要是通过标签来访问的。ValueStack
有一个特点,如果访问的值栈里有多个对象,且相同的属性在多个对象中同时出现,则值栈会按照从栈顶到栈底的顺序,寻找第一个匹配的对象。
4.3.1 如何获取
直接由ActionContext
对象的getValueStack()
方法即可获取
4.3.2 如何使用
ValueStack
主要的功能也是用来存放数据的,典型的方法如下:
-
Object findValue(String expr)
:根据表达式在value stack
中,按照缺省的访问顺序去获取表达式对应的值 -
void setValue(String expr, Object value)
:根据表达式,按照缺省的访问顺序,向value stack
中设置值 -
Object peek()
:获取value stack
中的顶层对象,不修改value stack
对象 -
Object pop()
:获取value stack
中的顶层对象,并把这个对象从value stack
中移走 -
void push(Object o)
:把对象加入到value stack
对象中,并设置成为顶层对象
4.3.3 应用示例
前面的示例中,欢迎页面显示的帐号,是从登录页面填写并传递到后台的数据,假如现在想要修改在欢迎页面显示的帐号数据,但是前面从登录页面填写并传递到后台的数据不需要变化,那么该怎么实现呢?
先来分析一下,要想修改result
页面显示的值,肯定需要在Result
处理之前修改这个值,否则等Result
处理完成过后再改就没有意义了。因此,可以选用PreResultListener
的技术,在里面把值修改好,然后再进行Result
处理。另外一点,在欢迎页面是通过标签来获取帐号的数据并展示的,也就是说值的来源是value stack
,因此,在PreResultListener里面要修改的就是value stack
里面的值。好了,清楚该干什么过后,来具体看看示例。
先来实现PreResultListener
,在里面修改value stack
里面的值,示例如下:
public class MyPreResult implements PreResultListener{
public void beforeResult(ActionInvocation actionInvocation, String result) {
System.out.println("现在处理Result执行前的功能,result="+result);
actionInvocation.getInvocationContext().getValueStack().setValue("account", "被修改了");
}
}
实现了PreResultListener
,还需要在运行之前注册,这里选择在Action
里面来注册这个监听器,示例如下:
public class HelloWorldAction extends ActionSupport {
private String account;
private String password;
private String submitFlag;
public String execute() throws Exception {
this.businessExecute();
ActionContext c = ActionContext.getContext();
MyPreResult pr = new MyPreResult();
c.getActionInvocation().addPreResultListener(pr);
return "toWelcome";
}
/**
* 示例方法,表示可以执行业务逻辑处理的方法,
*/
public void businessExecute(){
System.out.println("用户输入的参数为==="+"account="+account+",password="+password+",submitFlag="+submitFlag);
}
//属性对应的getter/setter方法,省略了
}
struts.xml
、登陆界面和欢迎界面都不变。欢迎界面中的<s:property value="account"/>
就可以获得MyPreResult
类中修改的account
值了。
由于通常情况下,向value stack
里面压入值都是由Struts2
去完成,而访问value stack
多是通过标签中的OGNL
表达式,因而直接使用ValueStack
的机会并不是很多。