Struts2中值栈ValueStack的使用
1、什么是值栈?
值栈ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类。
当客户端发起一个请求时,Struts2架构会创建一个action实例对象,同时也会创建一个OgnlValueStack值栈实例对象,这个值栈对象将会贯穿整个Action的生命周期。
Struts2中使用OGNL将请求Action的参数封装为对象,存储到值栈中,可以通过OGNL表达式读取值栈中对象的属性值。
注意:
(1)ValueStack是接口,OgnlValueStack是实现类。
(2)值栈是Strust的一个容器,用于存取数据,基本不再使用域对象了。
(3)一次请求,创建一个Action实例对象和一个ValueStack对象。
(4)ValueStack对象的生命周期和对应的Action对象一样,请求响应后结束。
2、值栈的内部结构
(1)值栈(OgnlValueStack)的内部主要有两大区域:root 和 context。
CompoundRoot root;
transient Map<String, Object> context;
(2)root类型是Compound,这个类继承了ArrayList,是一个List集合。
public class CompoundRoot extends ArrayList { }
(3)context类型是Map<String, Object>,是一个HashMap集合。
(4)root中通常存放的是java对象。
context中放的是web开发常用的对象的引用(root, request, session, application, parameters, attr等)。也包括root的引用,所以说通过context也能获取root对象。
注意:
获取root中的数据,不需要加 # 号。
获取context中的数据,需要加 # 号。
我们所说的操作值栈,通常指的是操作ValueStack的root区域的数据。
3、ActionContext 和 ValueStack的关系
ActionContext:Action的上下文。
通过查看源码可以发现,当一个请求过来时,执行核心过滤器对象中的doFilter()方法,在这个方法中创建了ActionContext对象。
在创建ActionContext对象的过程中,会创建ValueStack对象,并将ValueStack对象传递给了ActionContext对象。所以我们可以通过ActionContext对象来获取到值栈ValueStack对象。
ActionContext对象之所以能够访问Servlet的API(访问的是域对象的数据,并不是域对象本身),是因为ActionContext内部有值栈的引用。
4、获取值栈对象
获取值栈对象有2种方法:
(1)通过ActionContext对象获取值栈。
(2)在Struts2的内部,将值栈也存入了request中。可以通过request对象获取值栈。
测试代码:
package com.pipi.struts2.valuestack;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.ServletActionContext;
import javax.servlet.http.HttpServletRequest;
// 获取ValueStack对象
public class ValueStackDemo1 extends ActionSupport {
@Override
public String execute() throws Exception {
// 第一种:通过ActionContext对象获取
ValueStack valueStack1 = ActionContext.getContext().getValueStack();
// 第二种:通过request对象获取
HttpServletRequest request = ServletActionContext.getRequest();
ValueStack valueStack2 = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
System.out.println(valueStack1 == valueStack2); // true
return NONE;
}
}
一个Action实例中,只对应一个ValueStack对象。
5、向值栈中存入数据
存入数据有两种方式:
第一种:在Action中定义属性,并提供get方法。
第二种:使用ValueStack中本身的push(), set()方法(常用的)
(1)在Action中定义属性,并提供get方法。
默认情况下,Action对象本身都会压入到ValueStack中去,当然Action的属性也在里面。
先定义一个User类:
public class User {
private String name;
private Integer age;
//.....
}
Action类:
package com.pipi.struts2.valuestack;
import com.opensymphony.xwork2.ActionSupport;
// 向值栈中存入数据
public class ValueStackDemo2 extends ActionSupport {
// 方式一:定义属性,提供set和get方法
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
user = new User("张三", 18);
return SUCCESS;
}
}
JSP页面:
<s:property value="user.name" />
<s:property value="user.age" />
当传递的数据过多时,定义属性显然会显得代码繁琐。
(2)第二种:使用ValueStack中本身的方法(常用的)
Action类:
package com.pipi.struts2.valuestack;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
// 向值栈中存入数据,使用ValueStack本身的方法
public class ValueStackDemo3 extends ActionSupport {
@Override
public String execute() throws Exception {
// 先获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
// 存入数据使用:push(Object); set(String key, Object value);
// 一般存入对象使用push(),存入集合使用set()
User user = new User("张三", 20);
// 手动压栈,user对象会在栈的顶部
valueStack.push(user);
return SUCCESS;
}
}
JSP页面:
<s:property value="name" /><br />
<s:property value="age" /><br />
6、从值栈中获取数据
(1)获取值栈root区的一个对象的数据
(2)获取值栈root区的一个集合的数据(常用的)
(3)获取值栈context区,即引用域对象中的数据
Action类:
package com.pipi.struts2.valuestack;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.ServletActionContext;
import java.util.ArrayList;
import java.util.List;
// 向值栈ValueStack中存一个对象,存一个集合,在JSP页面取出
public class ValueStackDemo4 extends ActionSupport {
@Override
public String execute() throws Exception {
// 获取值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
// 向值栈中存一个对象
valueStack.push(new User("张三", 33));
// 向值栈中存一个集合
List<User> list = new ArrayList<>();
list.add(new User("李四", 44));
list.add(new User("王五", 55));
list.add(new User("赵六", 66));
valueStack.set("userList", list);
// 向值栈的context中存数据,即向域对象里存数据
ServletActionContext.getRequest().setAttribute("userList1", list);
ServletActionContext.getRequest().getSession().setAttribute("userList2", list);
ServletActionContext.getServletContext().setAttribute("userList3", list);
return SUCCESS;
}
}
JSP页面:
<h3>从值栈的root区取出一个对象的数据</h3>
<s:property value="name" /><br />
<s:property value="age" /><br />
<hr />
<h3>从值栈的root区取出一个集合的数据</h3>
<s:property value="userList[0].name" /><br />
<s:property value="userList[0].age" /><br />
<s:property value="userList[1].name" /><br />
<s:property value="userList[1].age" /><br />
<s:property value="userList[2].name" /><br />
<s:property value="userList[2].age" /><br />
<hr />
<h3>从值栈的context区取出域对象中的数据</h3>
<s:property value="#request.userList1[0].name" /><br />
<s:property value="#request.userList1[1].name" /><br />
<s:property value="#request.userList1[2].name" /><br />
<s:property value="#session.userList2[0].name" /><br />
<s:property value="#session.userList2[1].name" /><br />
<s:property value="#session.userList2[2].name" /><br />
<s:property value="#attr.userList1[0].name" /><br />
<hr />
运行截图: