Struts2值栈

一、咄咄怪事

1.奇怪的EL表达式

给一个目标Action类发送一个Struts2请求,假设目标Action类中包含如下方法:

public String getMessage() {

return "I am very happy in atguigu!";

}

则在结果JSP页面上可以使用如下EL表达式获取上述方法的返回值:

${requestScope.message }

 

但毫无疑问我们根本没有将任何数据以message为属性名保存到请求域中,那么这个奇怪的EL表达式是如何读取到数据的呢?

 

2.偷梁换柱的getAttribute()方法

我们逐步来探究一下上面提出的问题。

 

①第一步

根据EL表达式语法,${requestScope.message }会被翻译成如下代码:

request.getAttribute("message");

 

②第二步

在页面上直接输出request对象得到如下结果:

org.apache.struts2.dispatcher.StrutsRequestWrapper@756ea3b2

 

而由Tomcat创建的request对象本来应该是:

org.apache.catalina.connector.RequestFacade@39e2cdaa

 

说明request对象已经不是我们在纯Servlet容器环境下使用的request对象了,而是一个经过Struts2包装过的request对象。

 

③第三步

StrutsRequestWrapper通过继承javax.servlet.http.HttpServletRequestWrapper类对原始的request对象进行了包装,仅修改了getAttribute()一个方法的行为。代码如下:

/**

 * 首先从原始的请求域中获取属性值,如果找不到就从值栈中读取

 *

 * @param key 请求域数据的键

 */

public Object getAttribute(String key) {

    if (key == null) {

        throw new NullPointerException("You must specify a key value");

    }

 

    //如果禁用了从值栈读取数据的功能或key是以javax.servlet开始的则从原始的请求域中读取数据并直接返回

    if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {

        return super.getAttribute(key);

    }

 

    //获取ActionContext对象

    ActionContext ctx = ActionContext.getContext();

 

    //尝试从原始的请求域中读取数据

    Object attribute = super.getAttribute(key);

 

    //如果ActionContext对象不为空且从原始的请求域中没有获取到指定数据

    if (ctx != null && attribute == null) {

 

        boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));

 

        if (!alreadyIn && !key.contains("#")) {

            try {

                ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);

 

                //通过ActionContext对象获取“值栈”对象

                ValueStack stack = ctx.getValueStack();

 

                if (stack != null) {

                 //如果值栈对象不为空,则尝试根据key“查找”数据

                    attribute = stack.findValue(key);

                }

            } finally {

                ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);

            }

        }

    }

    return attribute;

}

结论就是在Struts2环境下,request对象的getAttribute()方法会首先从请求域中获取数据,如果获取不到,则通过ValueStack对象获取数据。

 

 

本教程由尚硅谷教育大数据研究院出品,如需转载请注明来源,欢迎大家关注尚硅谷公众号(atguigu)了解更多。