一.什么是值栈
1.ValueStack是Struts2中提供的一个接口,只有一个实现类OgnlValueStack,我们平时使用的OGNL表达式就是从值栈中获取值.
2.我们编写的每一个Action都有与之对应的值栈对象,也就是说每一次Action请求都会产生一个ValueStack对象
3.Struts2框架把ValueStack保存在名称为struts.valueStack的request属性中
二.值栈的内部结构
1.值栈由两部分组成:root+contextMap:
root():是一个List结构,Struts2会把Action和一些对象压入Root中
contextMap:是一个map集合,保存的是一些映射,其中request,session,application的值都会保存到contextMap这个大Map中
2.contextMap的组成:Struts 会把下面这些映射压入 ContextMap 中
parameters: 该 Map 中包含当前请求的请求参数 ?name=xxx&password=123
request: 该 Map 中包含当前 request 对象中的所有属性
session: 该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前 application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session, application
3.ValueStack 和 ActionContext 是什么关系 ?
class A{
B b;
}
class B{
A a;
}
ValueStack 和 ActionContext,是互相引用.值栈对象是请求时创建的:
doFilter中 prepare.createActionContext(request, response);
创建ActionContext 对象过程中,创建 值栈对象ValueStack
ActionContext对象 对 ValueStack对象中的context是 有引用的 (在程序中 通过 ActionContext 获得 值栈对象 )
ActionContext.getContext();返回的ActionContext对象。(还有值栈的引用)
Dispatcher类 serviceAction 方法中 将值栈对象保存到 request范围
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
在Struts2的创建ActionContext在核心控制器执行时创建:
4.获得值栈对象 有两种方法
ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
5.向值栈保存数据 (默认是向 root中保存数据)alueStack.push(Object obj);push方法的底层调用root对象的push方法(把元素添加到0位置)
ValueStack源码片段:
/**
* @see com.opensymphony.xwork2.util.ValueStack#push(java.lang.Object)
*/
public void push(Object o) {
root.push(o);
}
CompoundRoot源码片段:
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);
}
}
valueStack.set(String key, Object obj);源码获取map集合(map有可能是已经存在的,有可能是新创建的),把map集合push到栈中,再把数据存入到map集合中。
ValueStack中set方法原理:
/**
* @see com.opensymphony.xwork2.util.ValueStack#set(java.lang.String, java.lang.Object)
*/
public void set(String key, Object o) {
//set basically is backed by a Map pushed on the stack with a key being put on the map and the Object being the value
Map setMap = retrieveSetMap();
setMap.put(key, o);
}
private Map retrieveSetMap() {
Map setMap;
Object topObj = peek();
if (shouldUseOldMap(topObj)) {
setMap = (Map) topObj;
} else {
setMap = new HashMap();
setMap.put(MAP_IDENTIFIER_KEY, "");
push(setMap);
}
return setMap;
}
在jsp中 通过 <s:debug /> 查看值栈的内容
6.值栈理解图解:
7.获取值栈中的数据:
一般情况向我们是将对象利用root的push的方式压入栈顶,这时候获取值的时候,直接根据对象的属性获取即可:
后台代码:
前台获取:username和age为user的属性ValueStack stack = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack"); User user = new User(); stack.push(user);
如果压入的数据是集合形式,我们一般使用set的方式将数据压入栈顶,然后在前台进行获取,根据键的名称获取:<s:property value="username"/> <s:property value="age"/>
后台代码:
前台获取:ValueStack stack = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack"); stack.set("name","张三");
<s:property value="name"/>
8.EL表达式为什么可以从值栈中取值:
EL表达式可以从值栈中取值的原因在于,Struts2对request进行了增强,而EL表达式取值的本质就是调用request的getAttribute.Struts2中的request.getAttribute首先会进行普通的获取值,如果没有拿到则会去值栈中从上往下扫描,所以EL表达式会获取到值栈中的数据.