• Struts2 之值栈


    值栈(ValueStack)

    http://www.cnblogs.com/bgzyy/p/8639893.html 这是我的有关 struts2 的第一篇文章,对于里面我们说到的一个 struts2 HelloWorld 小练习,即在输入框输入信息提交后在另外一个页面显示输入的信息,显示页面的代码如下:

    UserName: ${userName}<br>
    Email: ${email}<br>
    Address: ${address}<br>
    

      为什么这样一个简单的标签就可以获取到另外一个页面的输入信息,我们使用上面链接中的代码并在其基础上加以改进以得到答案!

      我们知道 struts 默认的请求类型为 dispatcher,即请求转发,那么我们尝试在 show.jsp 中利用 request 域对象打印输入值,如下(在前面加上标识以区分):

    UserDesc: ^+^<%= request.getAttribute("userDesc")%><br>
    

      结果如下图:

    • 我们可以看到利用 request 的 getAttribute() 方法打印的结果和使用标签一样,此时我们应该想到将 request 打印出来,代码以及结果如下:

        Request: <%= request%>
      

    如上图所示,此时的 request 是已经被 struts2 封装的 request,在 IDEA 中双击 Shift 查找 StrustsRequestWrapper 源代码,找到其 getAttributte() 方法,如下:

    public class StrutsRequestWrapper extends HttpServletRequestWrapper {
    	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;
    	}
    }
    
    • 如上代码所示,我们可以知道 StrutsRequestWrapper 继承自 HttpServletRequestWrapper
    • getAttribute() 方法首先判断传入的 key 是否为空,若是抛出空指针异常
    • 若不是判断传入的 key 值是否满足一定的条件,若满足则直接使用父类的 getAttribute() 方法,获取对应的属性值
    • 若不满足则经过一系列判断后获取到 ValueStack 对象 stack,从 stack 对象中获得对应 key 的属性,并返回

      为了一探究竟我们 Debug 一步步调试查看,首先 Debug 运行程序,在输入页面输入信息之后再在源代码页面上打断点(在源代码页面的 ValueStack 前一行打断点),再点击提交将会跳转到调试页!

    • 第一次运行至断点结果如下图所示,这是 struts2 初始化一些必要的信息

    • 将光标放置在断点行,点击 Run to Cursor(运行至光标处),直到 key 的值为 userName,再点击将依次 userDesc 等,如图

    • 此时点击Step Over 执行代码到下一行,ValueStack 对象将被初始化,如下图所示,在这里我们依次打开 stack,root 在这里我们可以看到一对一对的 key 和 value ,进而我们得知显示页面的值是从此处得来的

    • 一些关于值栈的概念
      • ValueStack(值栈):贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个 ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象.
      • 在 ValueStack 对象的内部有两个逻辑部分,ObjectStatck 和 ContextMap;
      • struts 把 Action 和相关对象(如上例中的 Info 对象)压入ObjectStack 中,这里所说的 ObjetcMap 即上图中的 root,遵循“先进后出” 的原则
      • ContextMap:Struts 把各种映射关系压入 ContextMap 中,实际上就是一些对 ActionContext 的引用(parameters、request、session、application、attr)

      至此我们得知显示页面的底层实现,即从 ValueStack 中获取,其默认从栈顶开始寻找与 key 值匹配的属性,依次往下,也了解到值栈的基本概念,接下来让我们着手利用 OGNL 获取值栈里对象的属性。

    OGNL

    • 在 JSP 页面上利用 OGNL 访问值栈里对象的属性,若希望访问值栈中 ContextMap 中的数据,需要给 OGNL 表达式前面加上一个前缀 #,如果没有添加将会在 ObjectStack 中进行,如下示例在 session 内找 key 为 sessionMap 的属性

        <s:property value="#session.sessionMap"/>
      
    • property 标签

      • Struts2 的 property 标签用来输出值栈中的一个属性值
      • 其属性 value 表示来自栈顶对象在页面上将要显示的值(String 类型)
      • 其属性 default 表示若 value 若为空,将显示该值(String 类型)
      • 其属性escape 表示是否对 HTML 特殊字符进行转义
    • 读取规则

      • 读取 ObjectStack 里的对象的属性,ObjectStack 里的对象可以通过一个从零开始的下标来引用,即可以使用[0].userName 来返回栈顶对象的 message 属性,结合 <s:property> 标签

      • 若在指定的对象中没有找到指定的属性,则到指定对象的下一个对象里继续搜索,即 [n] 的意义是从第 n 个开始搜索,而不是只搜索第 n 个

      • 若从栈顶对象开始搜索则可以省略下标

      • 默认情况下 Action 对象会被 Struts2 自动的放到值栈的栈顶

          // 如下两种写法都是从栈顶开始在对象栈中查找 key 为 userName 的属性
          <s:property value="userName"/>
          <s:property value="[0].userName"/>
        
  • 相关阅读:
    设计模式-迭代器模式(Iterator)
    设计模式-责任链模式(responsibility)
    设计模式-中介者模式(Mediator)
    设计模式-解释器模式(Interpreter)
    设计模式-适配器模式(Adapter)
    第十章——维护索引(8)——在计算列中创建索引提高性能
    第十章——维护索引(7)——使用索引视图提高性能
    第十章——维护索引(6)——查找无用索引
    第十章——维护索引(5)——查找丢失索引
    第十章——维护索引(4)——通过重组索引提高性能
  • 原文地址:https://www.cnblogs.com/bgzyy/p/8761414.html
Copyright © 2020-2023  润新知