每次请求一个action都生成一个狭义的值栈,即ValueStack,而且这个值栈存储了我们定义在这个action里面的每个属性的值,这些属性必须是有get和set方法的,这样我们在jsp页面就可以用EL表达式直接访问这些属性的值,ActionContext可以看做一个广义的值栈,它里面包含了ValueStack,而且还包含了其他信息,所以说是广义的,用EL表达式先从ValueStack里面找,没有就去广义的值栈找。(经过测试,我发现属性不实现get和set方法也可以用EL表达式访问到,但是用debug查询的话,发现这个action对象对应的属性里面没有这个属性和它的值,实现了get和set方法的属性就有)
值栈是什么
简单的说:值栈是对应每一个请求对象的轻量级的内存数据中心。
Struts2中一个很激动人心的特性就是引入了值栈,在这里统一管理着数据,供Action、Result、Interceptor等Struts2的其他部分使用,这样一来,数据被集中管理起来而不会凌乱,大大方便了程序编写。
Struts2中关于值栈的另外一个很激动人心的特性就是:大多数情况下,你根本无需关心值栈,你不用管它在哪里,不用管它里面有什么,你只需要去获取自己需要的数据就可以了。也就是说,你可以隐式的使用值栈。
当然,如果编写自定义的Result或拦截器等较复杂功能的时候,还是需要显示访问值栈的,因此,还是需要你掌握值栈的知识。
7.1.2 值栈能干什么
简单的说,值栈能够线程安全的为每个请求提供公共的数据存取服务。
7.1.3 值栈有什么
事实上,到现在为止,我们一直在讲“值栈”,这种说法其实是不够准确的。为什么呢?因为在Struts2中,值栈又有广义和狭义之分:
1:狭义值栈
狭义值栈里面存放着一些OGNL可以存取访问的数据,典型如:
- Action的实例,这样就可以通过OGNL来访问Action实例中的属性的值了
- OGNL表达式运算的值,可以设置到值栈中,可以主动访问值栈对象,强行设置
- OGNL表达式产生的中间变量,比如在后面使用Struts2的标签的时候,使用循环标签,自然会有循环的变量,这些都存放在值栈中
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去交互,但是Action需要能访问到Web应用的数据,不仅仅是取得请求参数的值,往往也需要在Action里直接获取请求或会话的一些数据,对于这些数据,现在都可以通过ActionContext来获取到。
3:关于广义和狭义
你会看到,在ActionContext里面其实是包含着狭义值栈的,正是因为这个原因,再加上ActionContext还包含其他的数据,因此把ActionContext称为广义值栈。
今后在说值栈的时候,没有特别指明的情况下,多数就是指的广义值栈,反正开发的时候都是说从值栈中获取值。当然,有一种情况除外,就是在页面上使用OGNL的时候,没有特殊标识的情况下,默认是从value statck中取值的。
7.1.4 ActionContext的基本使用
前面学习了值栈的基本知识,接下来,看看在程序中具体如何使用值栈。
1:如何获取
要获取ActionContext有两个基本的方法,如果在不能获取到ActionInvocation的地方,可以直接使用ActionContext一个静态的getContext方法,就可以访问到当前的ActionContext了,示例如下:
如果在能获取到ActionInvocation的地方,比如在拦截器里面、自定义的Result里面等,可以通过ActionInvocation来获取到ActionContext,示例如下:
2:获取过后,如何使用
- get(String key):根据key从ActionContext当前的存储空间里面获取相应的值
- put(String key, Object value):把值存储在ActionContext的存储空间里面
- Map getApplication():返回ServletContext中存储的值
- Map getSession():返回HttpSession中存储的值
- Map getContextMap():返回当前context存储的值
- Map getParameters():返回HttpServletRequest对象里面存储的,客户端提交的参数
- ValueStack getValueStack():获取OGNL的值栈
对于getXXX的方法,都有对应的setXXX方法,这里就不去赘述了,具体的请参看Struts2的API文档。
3:应用示例
7.1.5 ValueStack的基本使用
在上一小节中,看到了ValueStack被包含在ActionContext中,ValueStack也是用来存储对象的,但是它主要是通过OGNL表达式来访问,也就是说,在Struts2里面主要是通过标签来访问的。
ValueStack有一个特点,如果访问的值栈里有多个对象,且相同的属性在多个对象中同时出现,则值栈会按照从栈顶到栈底的顺序,寻找第一个匹配的对象。
1:如何获取
2:如何使用
- 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对象中,并设置成为顶层对象
3:应用示例
先来分析一下,要想修改result页面显示的值,肯定需要在Result处理之前修改这个值,否则等Result处理完成过后再改就没有意义了。因此,可以选用PreResultListener的技术,在里面把值修改好,然后再进行Result处理。
另外一点,在欢迎页面是通过标签来获取帐号的数据并展示的,也就是说值的来源是value stack,因此,在PreResultListener里面要修改的就是value stack里面的值。
好了,清楚该干什么过后,来具体看看示例。
(1)先来实现PreResultListener,在里面修改value stack里面的值,示例如下:
(2)实现了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方法,省略了 - }
(3)相应的struts.xml就比较简单了,示例如下:
- <</span>package
name="helloworld" extends="struts-default"> -
"helloworldAction" class="cn.javass.action.action.HelloWorldAction"> -
"toWelcome">/s2impl/welcome.jsp -
-
</<span class="keyword" style="FONT-WEIGHT: bold; COLOR: #006699; PADDING-BOTTOM: 0px; PADDING-TOP: 0px; PADDING-LEFT: 0px; MARGIN: 0px; PADDING-RIGHT: 0px">package>
(4)登录页面没有变动,示例如下:
(5)再看看欢迎页面,也没有变动,示例如下:
(6)去运行测试看看,欢迎页面显示出来的就应该是修改过后的值了,如下图所示:
图7.1 修改result数据的欢迎页面
由于通常情况下,向value stack里面压入值都是由Struts2去完成,而访问value stack多是通过标签中的OGNL表达式,因而直接使用ValueStack的机会并不是很多。