1 什么是ValueStack?
Struts2将XWork对Ognl的扩展这一套机制封装起来,这个对象叫ValueStack(即值栈),其实ValueStack是Struts2的一个接口,OgnlValueStack是ValueStack的实现类,客户端发起一个请求时,Struts2会创建一个action的实例并同时创建一个OgnlValueStack的值栈实例,OgnlValueStack贯穿了整个action的生命周期,struts2中使用OGNL将请求action的参数封装为对象存储在值栈中,并通过OGNL表达式读取值栈中的对象属性值
2 值栈的简要概括
ValueStack是一个容器,其实就类似于一个数据中转站(Struts2框架中的数据就都保存到了ValueStack中)
(1)valuestack是接口,OgnlValueStack是接口的实现类;
(2)ValueStack贯穿整个Action生命周期(Action一旦创建了,框架就会创建一个valueStack对象)
注意:只要存入值栈,在任何地方都能取出数据(可以在页面,可以在Action,可以在配置文件中),之前的在request中存入数据,只能在jsp页面中取出数据
3 值栈的内部结构
ValueStack中最重要的两个区域:root区域和context区域
root区域(CompoundRoot):其实就是一个ArrayList,里面放置的是一个对象,获得root区域的数据不需要加#
package com.opensymphony.xwork2.util;
import java.util.ArrayList;
import java.util.List;
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
Context区域:其实就是一个Map。里面放置的是web开发的常用的对象数据的引用,获取context对象的需要加#。
(1)request(2)session(3)application(4)parameters(5)attr
4 值栈与ActionContext的关系
- ServletContext:Servlet的上下文
- ActionContext:Action的上下文
- 通过源码查看到:当请求过来的时候,执行核心过滤器中的dofilter方法,在这个方法中创建ActionContext,在创建ActionContext过程中,创建ValueStack对象,将ValueStack对象传递给ActionContext对象。所以可以通过创建ActionContext获得值栈对象。
- ActionContext对象之所以能够访问Servlet的API(访问的是域对象的数据,而不是真实的域对象)。因为在其内部有值栈的引用。
5 值栈的获得
5.1 通过ActionContext对象获得值栈
ValueStack valueStack1 = ActionContext.getContext().getValueStack();
5.2 通过request对象获得值栈
//有两种方式
//第一种
ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().
getAttribute("struts.valueStack");
//第二种
ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().
getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
6 操作值栈
所谓的操作值栈,通常指的是操作valuestack中的root区域
6.1 向值栈中存入数据
6.1.1 在Action中提供属性的get方法
- Action类的写法
package com.itykd.web.action;
import com.itykd.domain.User;
import com.opensymphony.xwork2.ActionSupport;
public class ValueStackDemo extends ActionSupport{
private User user;
public User getUser() {
return user;
}
public String execute() {
user = new User("ykd","1111111");
return SUCCESS;
}
}
- jsp页面
<body>
<h3>方式一:利用Action在值栈中的特性</h3>
<s:property value="user.username"/><br/>
<s:property value="user.password"/>
</body>
6.1.2 使用ValueStack中本身的方法的方式(调用值栈的方法实现)
- Action类
package com.itykd.web.action;
import com.itykd.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackDemo2 extends ActionSupport{
public String execute() {
//向值栈中保存数据
//获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
//使用push(Ojbect obj)或者set(String key,Ojbect obj)
User user = new User("ykd","222222");
//将user对象压入值栈后,user位于栈顶
valueStack.push(user);
return SUCCESS;
}
}
- jsp页面
<body>
<h3>方式二:调用值栈的方法实现</h3>
<s:property value="username"/><br/>
<s:property value="password"/>
<s:debug></s:debug>
</body>
可以利用<s:debug></s:debug>看到,目前的User对象已经在值栈的栈顶
6.2 获得值栈数据
获取值栈中的数据就是在页面中使用OGNL表达式即可
6.2.1 获得root的数据(不需要加#)
- Action类
package com.itykd.web.action;
import java.util.ArrayList;
import java.util.List;
import com.itykd.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackAction extends ActionSupport{
public String execute() {
//向值栈中保存一个对象
User user = new User("ykd", "13e123");
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.push(user);
//向值栈中保存一个List集合
List<User> list = new ArrayList<>();
list.add(new User("kobe","244645646"));
list.add(new User("Jordan", "23464654"));
list.add(new User("Duncan", "211e43dafd"));
valueStack.set("userList", list);
return SUCCESS;
}
}
- jsp页面
<body>
<h3>获得对象的数据</h3>
<s:property value="username"/>
<s:property value="password"/>
<h3>获得集合中的数据</h3>
<s:property value="userList[0].username"/>
<s:property value="userList[0].password"/>
<s:property value="userList[1].username"/>
<s:property value="userList[1].password"/>
<s:property value="userList[2].username"/>
<s:property value="userList[2].password"/>
</body>
6.2.2 获得context对象的数据(需要加#)
- Action类
package com.itykd.web.action;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class ValueStackAction2 extends ActionSupport{
public String execute() {
//向context中存入数据
ServletActionContext.getRequest().setAttribute("name", "ykd");
ServletActionContext.getRequest().getSession().setAttribute("name", "Kobe");
ServletActionContext.getServletContext().setAttribute("name", "Jordan");
return SUCCESS;
}
}
- JSP页面
<body>
<h3>向context中取数据</h3>
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#attr.name"/>
</body>
注意:attr.name会依次判断request、session、application域对象中是否有数据。如果有,则显示对应的数据
7 EL为何访问值栈数据
因为Struts2框架的底层对request.getAttribute(String name)进行了增强(StrutsRequestWrapper.class)