Struts2_值栈
借用在前面演示 hello world 的时候的例子。
在show.jsp 页面那些字段的值到底是怎么取得呢?现在在show.jsp中输出request
<%@ page language="java" import="java.util.*" pageEncoding="utf-8" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
ID:${id}<br>
编号:${ dlh}<br>
姓名:${ name}<br>
部门:${ bmmc}<br>
<%=request %>
</body>
</html>
可见,request是经过Struts包装过后的。
ctrl+shift+t 查看StrutsRequestWrapper类的 源码 。
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
private static final String REQUEST_WRAPPER_GET_ATTRIBUTE = "__requestWrapper.getAttribute";
private final boolean disableRequestAttributeValueStackLookup;
......
/**
* Gets the object, looking in the value stack if not found
*
* @param key The attribute key
*/
public Object getAttribute(String key) {
if (key == null) {
throw new NullPointerException("You must specify a key value");
}
if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
// don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(key);
}
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(key);
if (ctx != null && attribute == null) {
boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
// note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && !key.contains("#")) {
try {
// If not found, then try the ValueStack
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(key);
}
} finally {
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
}
}
}
return attribute;
}
}
当从super.getAttribute(key)获取不到值时,会从ValueStack中去取,super即HttpServletRequestWrapper 。所以,页面输出的值是从值栈里面取到的值,而并非是request请求域中有这么一些属性值。
debug断点调式
ActionContext其实是对OgnlContext的包装。
root即为实际意义上的后进先出的一个栈;值栈在页面取值输出的时候,就是从root属性里取其所含的对象的属性的值。
其中root属性为CompoundRoot对象,查看其源码可知。
public class CompoundRoot extends CopyOnWriteArrayList<Object> {
private static final long serialVersionUID = 8563229069192473995L;
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);
}
}
其继承了list,进栈push添加到第一个,其前的往后移;出栈删除第一个,其后的往前移。
总结:
ValueStack(值栈):
I. 可以从 ActionContext 中获取值栈对象
II. 值栈分为两个逻辑部分
Map 栈: 实际上是 OgnlContext 类型, 是个 Map, 也是对 ActionContext 的一个引用. 里边保存着各种 Map: requestMap, sessionMap, applicationMap, parametersMap, attr
对象栈: 实际上是 CompoundRoot 类型, 是一个使用 ArrayList 定义的栈. 里边保存各种和当前 Action 实例相关的对象.是一个数据结构意义的栈.
贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个 ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象.
Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中
在 ValueStack 对象的内部有两个逻辑部分:
- ObjectStack: Struts 把 Action 和相关对象压入 ObjectStack 中
- ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中. 实际上就是对 ActionContext 的一个引用
Struts 会把下面这些映射压入 ContextMap 中
- parameters: 该 Map 中包含当前请求的请求参数
- request: 该 Map 中包含当前 request 对象中的所有属性
- session: 该 Map 中包含当前 session 对象中的所有属性
- application:该 Map 中包含当前 application 对象中的所有属性
- attr: 该 Map 按如下顺序来检索某个属性: request, session, application