先推荐一篇不错的博客:值栈的概述
一、什么是值栈
ValueStack是Struts2框架的一个接口,翻译为值栈。OgnlValueStack是ValueStack的实现类,客户端发起一个请求,struts2框架会创建一个action实例的同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个Action的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性。
二、值栈的内部结构
OnglValueStack的源码
public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {
CompoundRoot root;
transient Map<String, Object> context;
}
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);
}
}
由上面的源码可以看出,OgnlValueStack中包扩两部分,值栈和Map(即ognl的上下文)。
Context:即OgnlContext上下文,它是一个Map结构,上下文中存储了一些引用,parameters、request、session、application等,上下文的Root为CompoundRoot。
CompoundRoot:存储了action实例,它作为OgnlContext的Root对象。CompoundRoot继承ArrayList实现压栈和出栈功能,拥有栈的特点:先进后出,后进先出,最后压进栈的数据在栈顶。我们把它称为对象栈。
Struts2对原OGNL(OGNL表达式的详解)作出的改进就是Root使用了CompoundRoot(自定义栈),使用OgnlValueStack的findValue()方法可以在CompoundRoot中从栈顶向栈底查找对象的属性值。
CompoundRoot作为OgnlContext的Root对象,并且在CompoundRoot中的action实例位于栈顶,当读取action的属性值时会先从栈顶对象中找对应的属性,如果找不到则继续找栈中的其他对象,如果找到则停止查找。
三、ActionContext和ValueStack的关系
在创建ActionContext的时候,同时创建ValueStack的对象,将ValueStack对象给ActionContext。
ActionContext中有一个ValueStack的引用,同时ValueStack中也有一个ActionContext的引用。
四、获取值栈对象
1、通过ActionContext对象获取值栈
ValueStack stack = ActionContext.getContext().getValueStack();
2、通过request域获取值栈
ValueStack stack = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
五、操作值栈
1、对Action中的属性提供get方法的方式
因为Action本身就在值栈中,Action中的属性也就默认在值栈中了,所以我们可以通过对Action的属性提供的get方法的方式来操作值栈。
2、手动操作值栈
调用值栈的push和set方法对值栈进行操作。
OgnlValueStack中push和set的区别:
public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {
CompoundRoot root;
public void push(Object o) {
root.push(o);
}
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;
}
public Object peek() {
return root.peek();
}
private boolean shouldUseOldMap(Object topObj) {
return topObj instanceof Map && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null;
}
}
通过源码可以看出,push直接将对象压入CompoundRoot中,set如果第一次使用,会创建一个map对象,并将键值对设置进去,如果不是第一次,则会将键值对直接放入该map中。