【Struts】值栈详解

来源:互联网 发布:中文域名网站 编辑:程序博客网 时间:2024/06/15 09:07
基本介绍
     ValueStack是Struts2的一个接口,字面意义为值栈,OgnlValueStack是 ValueStack的实现类,客 户端发起一个请求,struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例, OgnlValueStack贯穿整个Action的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储 到值栈中,并通过OGNL表达式读取值栈中的对象属性值。

PS:这里先说明一个易混淆的问题,那就是ContextMap和值栈的关系,从广义上来讲,ContextMap中存入了一个值栈的键值对。值栈中又有一个ContextMap的引用,这样就形成了一个互相引用的关系。所以说从广义上来讲,无论是从ContextMap的角度来分析,还是从StackValue的角度来分析,都没问题。有很多文档是以StackValue为起点,逐步分析到ContextMap。不过官方文档,还是以ContextMap作为起点。

值栈的内部结构

OnglValueStack源码

[java] view plain copy
  1. public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {  
  2.     CompoundRoot root;  
  3.     transient Map<String, Object> context;  
  4. }  


CompoundRoot源码
[java] view plain copy
  1. public class CompoundRoot extends ArrayList {  
  2.     public CompoundRoot() {  
  3.     }  
  4.     public CompoundRoot(List list) {  
  5.         super(list);  
  6.     }  
  7.     public CompoundRoot cutStack(int index) {  
  8.         return new CompoundRoot(subList(index, size()));  
  9.     }  
  10.     public Object peek() {  
  11.         return get(0);  
  12.     }  
  13.     public Object pop() {  
  14.         return remove(0);  
  15.     }  
  16.     public void push(Object o) {  
  17.         add(0, o);  
  18.     }  
  19. }  

由以上源码可以看出,OnglValueStack有两部分,一部分是继承ArrayList实现的一个栈结构,一个就是之前介绍过的,在《ContextMap详解》中介绍过的ContextMap。

关于ContextMap的介绍,这里不再给出,可以参考之前的博客《ContextMap详解》,这里分析下栈结构,也就是CompoundRoot,也就是OGNL三大要素之一的根对象(root),可以参考《ONGL基本使用 》。



CompoundRoot
  • CompoundRoot:存储了action实例,它作为OgnlContext的Root对象。
 
     CompoundRoot继承ArrayList 实现压栈和出栈功能,拥有栈的特点,先进后出,后进先出,最后压进栈的数据在栈顶。
     struts2对原OGNL作出的改进就是Root使用CompoundRoot(自定义栈),使用OnglValueStack
的findValue方法可以在CompoundRoot中从栈顶向栈底查找对象的属性值。
     CompoundRoot作为OgnlContext的Root对象,并且在CompoundRoot中action实例位于栈顶, 当读取achon的属性值时会先从栈顶对象中查找对应的属性,如果找不到则继续查找栈中的其它对象, 如果未找到则到ContextMap中去查找,未找到,则返回null。     

案例
[java] view plain copy
  1. package com.pc.web.action;  
  2.   
  3. import com.itheima.domain.Student;  
  4. import com.opensymphony.xwork2.ActionContext;  
  5. import com.opensymphony.xwork2.ActionSupport;  
  6. import com.opensymphony.xwork2.util.ValueStack;  
  7.   
  8. /** 
  9.  * ValueStack存值取值操作 
  10.  * @author Switch 
  11.  * 当我们不操作值栈时,默认的栈顶对象是:当前执行的动作类 
  12.  */  
  13. public class ActionTest2 extends ActionSupport {  
  14.   
  15.     private String name="泰斯特";  
  16.     /** 
  17.      * @return 
  18.      */  
  19.     public String demo2(){  
  20.         //1.获取ActionContext  
  21.         //从当前线程上获取  
  22.         ActionContext context = ActionContext.getContext();  
  23.         //2.使用ActionContext中的方法获取ValueStack  
  24.         //context.get(ValueStack.VALUE_STACK)  
  25.         //context.get("com.opensymphony.xwork2.util.ValueStack.ValueStack");  
  26.         //   
  27.         //也可以通过request域获取值栈  
  28.         //ServletActionContext.getRequest().getAttribute("struts.valueStack");  
  29.         ValueStack vs = context.getValueStack();  
  30.         //3.压栈操作:把一个学生对象压入栈顶  
  31.         Student s = new Student();  
  32.         s.setName("switch");  
  33.         s.setAge(20);  
  34.         //压栈操作  
  35.         vs.push(s);  
  36.   
  37.         return SUCCESS;  
  38.     }  
  39.   
  40.     public String getName() {  
  41.         return name;  
  42.     }  
  43.   
  44.     public void setName(String name) {  
  45.         this.name = name;  
  46.     }  
  47. }  

[html] view plain copy
  1. <%@page import="com.opensymphony.xwork2.util.ValueStack"%>  
  2. <%@page import="com.opensymphony.xwork2.ActionContext"%>  
  3. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  4. <%@ taglib uri="/struts-tags" prefix="s" %>  
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  6. <html>  
  7. <head>  
  8. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  9. <title>ValueStack的操作</title>  
  10. </head>  
  11. <body>  
  12. <%--1、取出ValueStack中第一个name属性的值  
  13. 借助s:property标签来获取。  
  14. 取值栈中的数据:直接写属性名称,获取的就是属性值。不需要使用#号。  
  15. OGNL表达式在获取值栈数据时,默认情况下都是从栈顶逐个对象往下寻找,寻找属性一致的,把属性值取出来。  
  16. 只要找到之后,就不再继续寻找。  
  17.   
  18. OGNL表达式获取值栈中的数据,只能是根据属性名称获取属性值。  
  19. --%>  
  20. Name:<s:property value="name"/><br/>  
  21. Age:<s:property value="age"/>  
  22. <hr/>  
  23. <%--2、获取指定位置属性值  
  24.     使用的OGNL表达式是:[x].name  
  25.     x指的是值栈中对象的索引位置。  
  26. --%>  
  27. Name1:<s:property value="[0].name"/><br/>  
  28. Name2:<s:property value="[1].name"/><br/>  
  29. <hr/>  
  30. <%--3、当我们使用s:property标签时,没有使用value属性,获取的是栈顶对象。  
  31.     当我们不操作值栈时,默认的栈顶对象是:当前执行的动作类  
  32.  --%>  
  33. <s:property/>  
  34. <hr/>  
  35. <%--4、OGNL表达式在OGNL上下文中查找数据时涉及的方法  
  36.     不管是在Map中找,还是在值栈中找,对应的方法只有一个。  
  37.     ValueStack的对象中的findValue(String expr)方法。参数的含义:是一个OGNL表达式  
  38.  --%>  
  39.  <% ActionContext context = ActionContext.getContext();  
  40.      ValueStack vs = context.getValueStack();  
  41.      Object o1 = vs.findValue("name");  
  42.      out.println("name is "+o1);  
  43.      out.println("<br/>");  
  44.      Object o2 = vs.findValue("[1].name");  
  45.      out.println("name1 is "+o2);  
  46.      out.println("<br/>");  
  47.      Object o3 = vs.findValue("#session.sessionMap3");  
  48.      out.println(o3);  
  49.      out.println("<br/>");  
  50.      Object o4 = vs.findValue("#application.applicationAttr");  
  51.      out.println(o4);%>  
  52. <s:debug></s:debug>  
  53. </body>  
  54. </html>  

OgnlValueStack中push和set的区别

[java] view plain copy
  1. public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {  
  2.     CompoundRoot root;  
  3.     public void push(Object o) {  
  4.         root.push(o);  
  5.     }  
  6.   
  7.     public void set(String key, Object o) {  
  8.         //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  
  9.         Map setMap = retrieveSetMap();  
  10.         setMap.put(key, o);  
  11.     }  
  12.   
  13.     private Map retrieveSetMap() {  
  14.         Map setMap;  
  15.         Object topObj = peek();  
  16.         if (shouldUseOldMap(topObj)) {  
  17.             setMap = (Map) topObj;  
  18.         } else {  
  19.             setMap = new HashMap();  
  20.             setMap.put(MAP_IDENTIFIER_KEY, "");  
  21.             push(setMap);  
  22.         }  
  23.         return setMap;  
  24.     }  
  25.   
  26.     public Object peek() {  
  27.         return root.peek();  
  28.     }  
  29.   
  30.     private boolean shouldUseOldMap(Object topObj) {  
  31.         return topObj instanceof Map && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null;  
  32.     }  
  33. }  

PS:通过源码可以看出,push直接将对象压入CompoundRoot中,set如果第一次使用,会创建一个map对象,并将键值对设置进去,如果不是第一次,则会将键值对直接放入该map中。