上篇文章中说了OGNL在Struts2中的使用方法。然而,EL其实也是可以读取值栈中的数据,那么EL读取值栈的数据和OGNL读取值栈的数据有什么不同呢?
还记得之前那个第八篇文章中的例子吗?代码都和它们一样,只是改一下getvalue.jsp
<s:iterator value="list" var="user">
<!-- 每次遍历出来的user对象,都会放到context中去,使用OGNL取root中的数据,
可以不用#,但取context中的数据,则要加上#
-->
<s:property value="#user.username"/>
<s:property value="#user.password"/>
</s:iterator>
<br>
使用jstl<br>
<c:forEach items="${list}" var="user">
${user.username}
${user.password}
</c:forEach>
两种方式输出的结果一样。
那么我们就来探讨一下为啥EL也能获取到值栈中的数据?是怎么获取的呢?
带着这个问题,我们来看看Struts2的默认核心控制器,因为它的实质是一个过滤器,过滤所有的页面,所以我们从它下手。
进入它的源码,看它的doFilter方法,里面有这么一句
request = prepare.wrapRequest(request);
这和request有联系,我们进入wrapRequest方法,里面只有这么一句话
request = dispatcher.wrapRequest(request);
那么我们继续进入这个wrapRequest方法,在该方法中,我们有这么一行
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
继续进入StrutsRequestWrapper类,终于到达目的地,该类中有这么一个方法
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
// 获取request中的属性值
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实例
ActionContext ctx = ActionContext.getContext();
// 通过key获取对应的值
Object attribute = super.getAttribute(key);
// 如果在request中没有找到对应的值,如果找到了,直接返回
if (ctx != null && attribute == null) {
if (!alreadyIn && !key.contains("#")) {
try {
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;
}
}
通过上面的代码,可以很清楚的看到EL表达式是怎么获取值栈中的值。
总结如下
其实是Struts2底层增强了getAttribute方法
(1)首先从request域获取值,如果找到了,直接返回
(2)如果没有找到,则去值栈中找,然后把值放入域对象中
可以看出使用EL表达式获取值栈中的值效率低,因为它经历了好多步。所以尽量避免使用EL去获取值栈中的值。