在 Struts2中,当一个Action(比如UserAction)将请求跳转(forward)到结果视图(比如result.jsp),有哪些方法可 以将Action的结果带到result.jsp中(即在result.jsp中可以获取到UserAction中的值)?
多种设值方法
1. UserAction的实例字段,只要提供了对应的getter方法,就可以在result.jsp中取到这个值
2. UserAction通过HttpServletRequest的setAttribute方法传值
ServletActionContext.getRequest().setAttribute("key1", "value1");
3. 通过ActionContext.getContext()的put方法,如:
ActionContext.getContext().put(“key1”,“value1”);
4. 通过ActionContext.getContext的ValueStack对象的push方法,如
ValueStack vs = ActionContext.getContext().getValueStack(); vs.setParameter("key1", "value1"); vs.push(new Date());
5. 通过ActionContext.getContext的ValueStack对象的setParameter方法,如
ValueStack vs = ActionContext.getContext().getValueStack(); vs.setParameter("key1", "value1");
6. 通过ActionContext.getContext的ValueStack对象的set方法,如
ValueStack vs = ActionContext.getContext().getValueStack(); vs.set("key1", "value1");
7. 通过ActionContext.getContext的ValueStack对象的setValue方法,如
ValueStack vs = ActionContext.getContext().getValueStack(); vs.setValue("key1", "value1");
问题:
这些API用起来很简单,但是有几个问题需要解决
1.这五种方式之间是否有某种关系,比如,直接往ActionContext中设值,和通过ActionContext往ValueStack中设值有什么区别和联系、
ActionContext.getContext().put(“key1”,“value1”); ValueStack vs = ActionContext.getContext().getValueStack(); vs.setParameter("key1", "value1");
2. 不同方式的设值的优先级,比如都存入一个键为key1的值,那么result.jsp取出时,以哪个值为准
3. 这些不同方式的设值,result.jsp在取出时,是否方式不一样?如果不一样,那么取值表达式的语法是什么?
4.ValueStack,顾名思义,是一个栈结构,也就是Value,先进后出,那么具体的用法是什么?
从Action存放数据的数据结构开始说起
Struts2中, Action相关的数据都存放在ValueStack中,ValueStack分为两部分,ActionContext和Compoud Root
1. ActionContext
a. ActionContext是一个Map数据结构,一方面存放用户通过ActionContext以及ServletActionContext设置的数据(key/value)
b. ActionContext也存放跟http请求相关的对象,比如application,session,request,page,parameters对象
c. 在result.jsp中,使用#属性名的方式告诉Struts,是要到ActionContext中取值,比如<s:property value="#key1"/>
2. CompoundRoot
a.CompoundRoot是一个堆栈数据结构,它继承自ArrayList,同时提供了push,pop等堆栈操作
b.当请求到某个Action时,Struts将这个Action对象放置于栈顶
c.当调用ValueStack的push操作时,会将push的对象放置于栈顶
d.在result.jsp中,使用属性名的方式告诉Struts(不加#),是要到CompoundRoot中取值。取值顺序是从栈顶到栈底依次查找,直到找到匹配给定属性名的属性
e.在result.jsp中,可是用#root[0]指向栈顶元素,使用#root[k]指向从栈顶开始的第k个元素,k从0开始
3. ValueStack的数据结构图
左侧的Object[0--n]以及ValueStack就是CompoundRoot对象的堆栈结构,右侧的ActionContext是一个Map+请求相关的对象(application,session,request,page)
实例
1.struts.xml配置
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="user" namespace="/user" extends="struts-default"> <action name="hi" class="com.tom.actions.UserAction"> <result name="dispatcher" type="dispatcher">/htmls/result.jsp</result> </action> </package> </struts>
2. UserAction
package com.tom.actions; import com.opensymphony.xwork2.ActionContext; import com.tom.model.User; import org.apache.struts2.ServletActionContext; public class UserAction { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String execute() { //通过action属性传值,此时HelloWorldAction位于CompoundRoot栈顶,可以直接获取,不用加# //注意:id属性被后面的ValueStack中的User对象覆盖 //<s:property value="id"/> //<s:property value="name"/> id = 1234; name = "tom"; //通过Context传值,因为是放在Context中,需要使用#访问 //<s:property value="#key1"/> //<s:property value="#key2"/> //<s:property value="#key3"/> ActionContext.getContext().put("key1", "value1"); ActionContext.getContext().put("key2", "value2"); ActionContext.getContext().put("key3", "value3"); //通过ValueStack访问,此时push对象将压栈到CompoundRoot的栈顶,此对象成为根对象 //也就是说,ValueStack的push操作会将对象压到CompundRoot的栈顶 //<s:property value="name"/> //<s:property value="id"/> User user = new User(); user.setId(2345); ActionContext.getContext().getValueStack().push(user); //************************************************************************************************ //此时,CompoundRoot栈包含两个对象,栈顶的User对象以及栈底的UserAction对象 //当通过s:property取值时,是通过从栈顶到栈底的顺序一次查找满足条件的属性,后加入的对象属性将覆盖前面加入的属性 //通过<s:property value="id"/>能取到User对象的id属性,但是取不到UserAction的id属性 //通过<s:property value="#root[1].id"/>能够取到UserAction的id属性,也就是说,通过#root[k]可以取到距离栈顶k个元素的元素 //栈顶为#root[0],其它以此类推 //************************************************************************************************ //通过HttpServletRequest对象传值,因为request放置于context中,所以需要#访问 //<s:property value="#key4"/> ServletActionContext.getRequest().setAttribute("key4", "value4"); //如果栈顶是一个Map,那么将key/value设置到栈顶Map中 //如果栈顶不是一个Map,那么构造一个HashMap,push到栈顶中 // <s:property value="key5"/> User user5 = new User(); user5.setId(5); user5.setName("tom5"); ActionContext.getContext().getValueStack().set("key5", user5); //放置于栈顶Map中 ActionContext.getContext().getValueStack().setParameter("key6", 1000); User user7 = new User(); user7.setId(7); user7.setName("tom7"); //放置于栈顶Map中 ActionContext.getContext().getValueStack().setValue("key7", user7); return "dispatcher"; } }
3. result.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title></title> </head> <body> key1: <s:property value="#key1"/> <br/> key2: <s:property value="#key2"/> <br/> key3: <s:property value="#key3"/> <br/> id: <s:property value="id"/> <br/> name: <s:property value="#root[1].name"/> <br/> request.key4:<s:property value="#request.key4"/> <br/> key5:<s:property value="key5"/> <br/> key5-2:<s:property value="key5.name"/> <br/> key6:<s:property value="key6"/> <br/> key7:<s:property value="key7.id"/> <br/> top: <s:property value="#root[0]"/> <br/> <s:debug/> </body> </html>
结果
key1: value1 key2: value2 key3: value3 id: 2345 name: request.key4:value4 key5:com.tom.model.User@2fb710a3 key5-2:tom5 key6:1000 key7:7 top: [{key6=1000, key5=com.tom.model.User@2fb710a3, key7=com.tom.model.User@1ba555ce, com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY=}, com.tom.model.User@3a45590, com.tom.actions.UserAction@19ed62ce, com.opensymphony.xwork2.DefaultTextProvider@2ff1cfae] [Debug] Struts ValueStack Debug Value Stack Contents Object Property Name Property Value java.util.HashMap empty null com.tom.model.User id 2345 name null com.tom.actions.UserAction id 1234 name tom com.opensymphony.xwork2.DefaultTextProvider texts null Stack Context These items are available using the #key notation Key Value com.opensymphony.xwork2.dispatcher.HttpServletRequest org.apache.struts2.dispatcher.StrutsRequestWrapper@751e1ab5 com.opensymphony.xwork2.ActionContext.locale zh_CN com.opensymphony.xwork2.dispatcher.HttpServletResponse HTTP/1.1 200 Content-Type: text/html; charset=utf-8 com.opensymphony.xwork2.ActionContext.name hi com.opensymphony.xwork2.ActionContext.application Exception thrown: java.lang.NullPointerException com.opensymphony.xwork2.ActionContext.conversionErrors {} __component_stack [org.apache.struts2.components.Debug@b6b9d25]