`

struts2 ognl valuestack

 
阅读更多

http://struts2.group.iteye.com/group/wiki/1356-how-to-use-ognl-in-struts2

OGNL是XWork引入的一个非常有效的数据处理的工具。我们已经了解了OGNL的基本操作和OGNL的内部结构,接下来,我们来看看XWork对OGNL做了什么样的加强,以及OGNL的体系在Struts2中如何运转。

从例子开始 Top 编辑 下移 删除 更多

我们先从一个例子开始,看看数据在Struts2中是如何运转的。

Java代码 复制代码 收藏代码
  1. /**  
  2.  * @author Downpour  
  3.  */  
  4. public class User {   
  5.        
  6.     private Integer id;   
  7.        
  8.     private String name;   
  9.        
  10.     private Department department = new Department();   
  11.        
  12.         // setter and getters   
  13. }   
  14.   
  15. //=========================================================================   
  16.   
  17. /**  
  18.  * @author Downpour  
  19.  */  
  20. public class Department {   
  21.        
  22.     private Integer id;   
  23.        
  24.     private String name;   
  25.            
  26.         // setter and getters   
  27. }   
  28.   
  29. //=========================================================================   
  30.   
  31. <form method="post" action="/struts-example/ognl.action">   
  32.     user name: <input type="text" name="user.name" value="downpour" />   
  33.     department name: <input type="text" name="department.name" value="dev" />   
  34.     <input type="submit" value="submit" />   
  35. </form>   
  36.   
  37. //=========================================================================   
  38.   
  39. /**  
  40.  * @author Downpour  
  41.  */  
  42. public class OgnlAction extends ActionSupport {   
  43.   
  44.     private static final Log logger = LogFactory.getLog(OgnlAction.class);   
  45.   
  46.     private User user;   
  47.        
  48.     private Department department;   
  49.        
  50.     /* (non-Javadoc)  
  51.      * @see com.opensymphony.xwork2.ActionSupport#execute()  
  52.      */  
  53.     @Override  
  54.     public String execute() throws Exception {   
  55.         logger.info("user name:" + user.getName());   // -> downpour   
  56.         logger.info("department name:" + department.getName());   // -> dev   
  57.         return super.execute();   
  58.     }   
  59.   
  60.     // setter and getters   
  61. }   
  62.   
  63. //=========================================================================   
  64.   
  65. user name: <s:property value="user.name" />   
  66. department name: <s:property value="department.name" />   
  67.   
  68. //=========================================================================  
/**
 * @author Downpour
 */
public class User {
	
	private Integer id;
	
	private String name;
	
	private Department department = new Department();
	
        // setter and getters
}

//=========================================================================

/**
 * @author Downpour
 */
public class Department {
	
	private Integer id;
	
	private String name;
		
        // setter and getters
}

//=========================================================================

<form method="post" action="/struts-example/ognl.action">
    user name: <input type="text" name="user.name" value="downpour" />
    department name: <input type="text" name="department.name" value="dev" />
    <input type="submit" value="submit" />
</form>

//=========================================================================

/**
 * @author Downpour
 */
public class OgnlAction extends ActionSupport {

    private static final Log logger = LogFactory.getLog(OgnlAction.class);

    private User user;
    
    private Department department;
    
    /* (non-Javadoc)
     * @see com.opensymphony.xwork2.ActionSupport#execute()
     */
    @Override
    public String execute() throws Exception {
        logger.info("user name:" + user.getName());   // -> downpour
        logger.info("department name:" + department.getName());   // -> dev
        return super.execute();
    }

    // setter and getters
}

//=========================================================================

user name: <s:property value="user.name" />
department name: <s:property value="department.name" />

//=========================================================================


我们可以看到在JSP中,form中的元素input等,都使用OGNL的表达式作为name的值。而在form提交时,这些值都会被设置到Action中的Java对象中。而当Action转向到JSP时,Struts2的Tag又可以从Action的Java对象中,通过OGNL进行取值。

在这里,你看不到任何的OGNL的代码级别操作,因为这些都在Struts2内部进行了封装。而这些封装,都是建立在OGNL的基本概念,也就是根对象和上下文环境之上。下面就分别就这两个方面分别进行讲解。

ValueStack —— 对OGNL的加强 Top 编辑 上移 下移 删除 更多

细心的读者可能会发现,在上面的例子中,我们使用了不同的表达式,针对Action中的不同的Java对象进行设值。再结合上一讲我们所例举的OGNL的代码操作示例,我们有强烈的理由怀疑,Struts2在内部有可能执行了这样的操作,才使得页面到Action的设值工作顺利完成:

Java代码 复制代码 收藏代码
  1. // "user.name" as OGNL expression, action as OGNL Root object   
  2. Ognl.setValue("user.name", action, "downpour");   
  3. Ognl.setValue("department.name", action, "dev");  
// "user.name" as OGNL expression, action as OGNL Root object
Ognl.setValue("user.name", action, "downpour");
Ognl.setValue("department.name", action, "dev");


如果这个怀疑是正确的,那么我们就能得出这样一个结论:Struts2的Action是OGNL操作的根对象。

这个结论是我们从现象上推出来的,至于它到底正确与否,我们之后可以通过源码分析来进行验证,在这里先卖一个关子,姑且认为它是正确的。不过这个结论对我们来说非常重要,因为这个结论Struts2的Tag,JSTL和Freemarker等表示层元素获取Action中变量的值打下了坚实的基础。

在Struts2(XWork)中,不仅把Action作为OGNL操作的根对象,作为对OGNL的扩展,它还引入了一个ValueStack的概念。这个概念代表了什么呢?还是让我们看看Struts2的Reference怎么说:
Struts2 Reference 写道
The biggest addition that XWork provides on top of OGNL is the support for the ValueStack. While OGNL operates under the assumption there is only one "root", XWork's ValueStack concept requires there be many "roots".

很明显,ValueStack依照它的结构和作用,至少为我们提供两大特性:

1. ValueStack是一个堆栈结构,堆栈中的每个元素对于OGNL操作来说,都被看作是根对象。

2. 由于ValueStack是一个堆栈结构,所以其中的元素都是有序的,对于某个OGNL表达式来说,OGNL将自堆栈顶部开始查找,并返回第一个符合条件的对象元素。


这里我们有必要对第二点啰嗦几句,举个具体的例子来说(这个例子同样摘自Struts2的Reference):如果在ValueStack中有2个对象,分别是“动物”和“人”,这两个对象都具备一个属性,叫做name,而“动物”还有一个属性叫species,“人”还有个属性叫salary。其中,“动物”对象在ValueStack的栈顶,而“人”这个对象在栈底。那么看看下面的OGNL表达式将返回什么?

Java代码 复制代码 收藏代码
  1. species    // call to animal.getSpecies()   
  2. salary     // call to person.getSalary()   
  3. name       // call to animal.getName() because animal is on the top  
species    // call to animal.getSpecies()
salary     // call to person.getSalary()
name       // call to animal.getName() because animal is on the top


对于name这个属性,返回的将是“动物”的name,因为“动物”在栈顶,会被先匹配到OGNL的表达式。但是有的时候,你可能需要访问“人”的name属性,怎么办呢?你可以通过下面的方法:

Java代码 复制代码 收藏代码
  1. [0].name   // call to animal.getName()   
  2. [1].name   // call to person.getName()  
[0].name   // call to animal.getName()
[1].name   // call to person.getName()

Struts2中的OGNL上下文环境 Top 编辑 上移 下移 删除 更多

有了ValueStack,我们再来仔细研究一下Struts2中OGNL的上下文环境。

Struts2 Reference 写道
The framework sets the OGNL context to be our ActionContext, and the value stack to be the OGNL root object. (The value stack is a set of several objects, but to OGNL it appears to be a single object.) Along with the value stack, the framework places other objects in the ActionContext, including Maps representing the application, session, and request contexts. These objects coexist in the ActionContext, alongside the value stack (our OGNL root)


也就是说,ActionContext是Struts2中OGNL的上下文环境。它维护着一个Map的结构,下面是这个结构的图示:



其中,ValueStack是这个上下文环境中的根对象,而除了这个根对象以外,Struts2还在这个上下文环境中放了许多额外的变量,而这些变量多数都是被XWork封装过的Servlet对象,例如request,session,servletContext(application)等,这些对象都被封装成Map对象,随着ActionContext作用于整个Action执行的生命周期中。

在这里,或许有些读者会提出问题来,为什么好好的Servlet对象要在这里被封装成Map对象呢?我想原因可能有以下两个:

1. 对Struts2的Action彻底屏蔽Servlet容器,从而无需再使用底层Servlet API进行编程。你所面对的,将永远是一个又一个的Java对象。

2. 便于各种View技术,例如JSP,Freemarker,Velocity等对ValueStack中上下文环境,尤其是Servlet对象中的数据进行读取。试想,如果在这里不将HttpServletRequest,HttpSession等Servlet对象转化成Map,那么我们将很难通过OGNL表达式,对这些Servlet对象中的值进行读取。

Struts2中使用OGNL进行计算 Top 编辑 上移 版本控制 删除 更多

取值计算

有了上面的这些知识,我们就能非常容易的理解在Struts2中如何使用OGNL进行取值计算。

提问:在Struts2中,如何使用自身的Tag读取Action中的变量?

Struts2自身的Tag会根据value中的OGNL表达式,在ValueStack中寻找相应的对象。因为action在ValueStack的顶部,所以默认情况下,Struts2的Tag中的OGNL表达式将查找action中的变量。请注意,value中的内容直接是OGNL表达式,无需任何el的标签包装。

例如:<s:property value="user.name" />

提问:在Struts2中,如何使用自身的Tag读取HttpServletRequest,HttpSession中的变量?

在上面的知识中,我们知道,Struts2中OGNL的上下文环境中,包含request,session,application等servlet对象的Map封装。既然这些对象都在OGNL的上下文中,那么根据OGNL的基本知识,我们可以通过在表达式前面加上#符号来对这些变量的值进行访问。

例如:<s:property value="%{#application.myApplicationAttribute}" />
<s:property value="%{#session.mySessionAttribute}" />
<s:property value="%{#request.myRequestAttribute}" />
<s:property value="%{#parameters.myParameter}" />

在这里啰嗦一句,在Tag的value中包括%{开头和}结尾的字符串,不知道Struts2为什么要做出这样的设置,从源码上看,它似乎没有什么特别额外的作用:

Java代码 复制代码 收藏代码
  1.   
  2. if (value == null) {   
  3.             value = "top";   
  4.         }   
  5.         else if (altSyntax()) {   
  6.             // the same logic as with findValue(String)   
  7.             // if value start with %{ and end with }, just cut it off!   
  8.             if (value.startsWith("%{") && value.endsWith("}")) {   
  9.                 value = value.substring(2, value.length() - 1);   
  10.             }   
  11.         }   
  12.   
  13.         // exception: don't call findString(), since we don't want the   
  14.         //            expression parsed in this one case. it really   
  15.         //            doesn't make sense, in fact.   
  16.         actualValue = (String) getStack().findValue(value, String.class);   
  17.            
  18.         ......   
  19.   
  20. }  
if (value == null) {
            value = "top";
        }
        else if (altSyntax()) {
            // the same logic as with findValue(String)
            // if value start with %{ and end with }, just cut it off!
            if (value.startsWith("%{") && value.endsWith("}")) {
                value = value.substring(2, value.length() - 1);
            }
        }

        // exception: don't call findString(), since we don't want the
        //            expression parsed in this one case. it really
        //            doesn't make sense, in fact.
        actualValue = (String) getStack().findValue(value, String.class);
        
        ......

}


有兴趣的朋友可以研究一下,这一对符号的原理究竟是什么。

提问:在Struts2中,如何使用JSTL来读取Action中的变量?

这是一个历史悠久的问题。因为事实上,很多朋友(包括我在内)是不使用Struts2自身的标签库,而是使用JSTL的,可能因为JSTL标签库比较少,简单易用的原因吧。

我们知道,JSTL默认是从page,request,session,application这四个Scope逐次查找相应的EL表达式所对应的对象的值。那么如果要使用JSTL来读取Action中的变量,就需要把Action中的变量,放到request域中才行。所以,早在Webwork2.1.X的年代,我们会编写一个拦截器来做这个事情的。大致的原理是:在Action执行完返回之前,依次读取Action中的所有的变量,并依次调用request.setAttribute()来进行设置。具体的整合方式,请参考以下这篇文档:http://wiki.opensymphony.com/display/WW/Using+WebWork+and+XWork+with+JSP+2.0+and+JSTL+1.1

不过随着时代的发展,上面的这种方式,已经不再被推荐使用了。(虽然如此,我们依然可以学习它的一个解决问题的思路)目前来说,自从Webwork2.2以后,包括Struts2,都使用另外一种整合方式:对HttpServletRequest进行装饰。让我们来看一下源码:

Java代码 复制代码 收藏代码
  1. public class StrutsRequestWrapper extends HttpServletRequestWrapper {   
  2.   
  3.     /**  
  4.      * The constructor  
  5.      * @param req The request  
  6.      */  
  7.     public StrutsRequestWrapper(HttpServletRequest req) {   
  8.         super(req);   
  9.     }   
  10.   
  11.     /**  
  12.      * Gets the object, looking in the value stack if not found  
  13.      *  
  14.      * @param s The attribute key  
  15.      */  
  16.     public Object getAttribute(String s) {   
  17.         if (s != null && s.startsWith("javax.servlet")) {   
  18.             // don't bother with the standard javax.servlet attributes, we can short-circuit this   
  19.             // see WW-953 and the forums post linked in that issue for more info   
  20.             return super.getAttribute(s);   
  21.         }   
  22.   
  23.         ActionContext ctx = ActionContext.getContext();   
  24.         Object attribute = super.getAttribute(s);   
  25.   
  26.         boolean alreadyIn = false;   
  27.         Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");   
  28.         if (b != null) {   
  29.             alreadyIn = b.booleanValue();   
  30.         }   
  31.   
  32.         // note: we don't let # come through or else a request for   
  33.         // #attr.foo or #request.foo could cause an endless loop   
  34.         if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {   
  35.             try {   
  36.                 // If not found, then try the ValueStack   
  37.                 ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);   
  38.                 ValueStack stack = ctx.getValueStack();   
  39.                 if (stack != null) {   
  40.                     attribute = stack.findValue(s);   
  41.                 }   
  42.             } finally {   
  43.                 ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);   
  44.             }   
  45.         }   
  46.         return attribute;   
  47.     }   
  48. }  
public class StrutsRequestWrapper extends HttpServletRequestWrapper {

    /**
     * The constructor
     * @param req The request
     */
    public StrutsRequestWrapper(HttpServletRequest req) {
        super(req);
    }

    /**
     * Gets the object, looking in the value stack if not found
     *
     * @param s The attribute key
     */
    public Object getAttribute(String s) {
        if (s != null && s.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(s);
        }

        ActionContext ctx = ActionContext.getContext();
        Object attribute = super.getAttribute(s);

        boolean alreadyIn = false;
        Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");
        if (b != null) {
            alreadyIn = b.booleanValue();
        }

        // note: we don't let # come through or else a request for
        // #attr.foo or #request.foo could cause an endless loop
        if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {
            try {
                // If not found, then try the ValueStack
                ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
                ValueStack stack = ctx.getValueStack();
                if (stack != null) {
                    attribute = stack.findValue(s);
                }
            } finally {
                ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);
            }
        }
        return attribute;
    }
}


看到了嘛?这个类会在Struts2初始化的时候,替换HttpServletRequest,运行于整个Struts2的运行过程中,当我们试图调用request.getAttribute()的时候,就会执行上面的这个方法。(这是一个典型的装饰器模式)在执行上面的方法时,会首先调用HttpServletRequest中原本的request.getAttribute(),如果没有找到,它会继续到ValueStack中去查找,而action在ValueStack中,所以action中的变量通过OGNL表达式,就能找到对应的值了。

在这里,在el表达式广泛使用的今天,JSTL1.1以后,也支持直接使用el表达式。注意与直接使用struts2的tag的区别,这里需要使用el的表示符号:${}

例如:${user.name}, <c:out value="${department.name}" />

提问:在Struts2中,如何使用Freemarker等模板来读取Action中的变量以及HttpServletRequest和HttpSession中的变量?

Freemarker等模板在Struts2中有对应的Result,而在这些Result中,Freemarker等模板会根据ValueStack和ActionContext中的内容,构造这些模板可识别的Model,从而使得模板可以以他们各自的语法对ValueStack和ActionContext中的内容进行读取。

有关Freemarker对于变量的读取,可以参考Struts2的官方文档,非常详细:http://struts.apache.org/2.0.14/docs/freemarker.html

设值计算

Struts2中使用OGNL进行设值计算,就是指View层传递数据到Control层,并且能够设置到相应的Java对象中。这个过程从逻辑上说需要分成两步来完成:

1. 对于每个请求,都建立一个与相应Action对应的ActionContext作为OGNL的上下文环境和ValueStack,并且把Action压入ValueStack

2. 在请求进入Action代码前,通过某种通用的机制,搜集页面上传递过来的参数,并调用OGNL相关的代码,对Action进行设值。

上面的第一个步骤,在处理URL请求时完成,而第二个步骤,则涉及到另外一个XWork的核心知识:拦截器。所以有关Struts2使用OGNL进行设值计算的详细分析,将会在拦截器章节具体给出。
分享到:
评论

相关推荐

    Struts中的ognl和valueStack

    深入讲解Struts中的ognl和valueStack

    Struts用的ognl和valueStack(vs)实例

    Struts用的ognl和valueStack(vs)实例

    Struts2中关于ValueStack的一些操作

    3、 WebWork的ValueStack底层有第三方开源项目OGNL实现。所以EL也都遵循OGNL的规范。我们在开发中,几乎不需要知道OGNL的细节。 4、 WebWork为每一次请求构建一个ValueStack,并将所有相关的数据对象(例如:...

    Struts2中的参数传递

    我们知道,Struts2完成参数传递处理工作的基础是OGNL和ValueStack。而在这个 过程中,我也把Struts2所要做的工作大致归纳为两个方面: 1. 对OGNL操作进行封装,完成OGNL表达式所表示的值到Java对象的值传递机制 2. ...

    s20505_valueStack_OGNL

    Struts2 demo,使用struts2的ognl表达式传值,以及页面的数据的实时的刷新。

    linjin101#javaStudy#Struts2中的OGNL和值栈ValueStack1

    1、什么是值栈 2、值栈的内部结构 3、ActionContext和ValueStatck的关系 4、如何获取值栈对象 5、向值栈存数据 6、从值栈中获取数据

    OGNL表达归纳

    3.由于ValueStack(值栈)是Struts 2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性 4.为何使用EL表达式能够访问valueStack中对象的属性 原因...

    Struts2入门教程(全新完整版)

    四、OGNL与ValueStack(VS) 22 1.值栈入门 22 2.OGNL入门 24 3.普通方法访问 24 4.静态方法访问 24 5.默认类Math的访问 24 6.调用普通类的构造方法 25 7.集合对象初步 25 8.集合对象进阶 25 9.N语法top语法 26 10....

    Struts2练习Demo以及随笔

    Struts的开发步骤、OGNL、ValueStack、Action核心、Result基本原理、Struts2核心标记库、Struts2拦截器、自定义拦截器、UI标记、非UI标记、资源文件国际化等等实例全面使用。

    Struts2 in action中文版

    5.2 OGNL和Struts 2 85 5.2.1 OGNL是什么 85 5.2.2 OGNL如何融入框架 87 5.3 内建的类型转换器 89 5.3.1 立即可用的类型转换器 89 5.3.2 使用OGNL表达式从表单字段名映射到属性 90 5.4 自定义类型转换 101 5.4.1 ...

    struts.docx

    Struts2是处理访问服务器的请求 学习struts2的(重点) Struts.xml的配置 参数传递 Ognl ValueStack Action原理 Result原理 拦截器 struts将会完全的取代我们的servlet技术

    Struts2框架基础 二

    OGNL表达式 interceptor拦截器 valuestack的存入 /取出

    struts项目调优需要的ognl2.7的jar包

    ognl2.7相对于2.6在性能上有了“显著”的提升.所以该jar包能对struts2的调优有很大帮助,尤其是ValueStack中的操作比较多的情况下。

    OGNL表达式语言.txt

    OGNL表达式语言: 相对EL表达式,它提供了平时我们需要的一些功能,如: ...另外OGNL会设定一个根对象(root对象),在Struts2中根对象是ValueStack。 如果访问根对象中的对象的属性,则可以省略#命名空间。

    精讲Struts2

    精简全面讲解Struts 2.0Struts2产生背景 工作原理 实现步骤 标签 命名空间 转换器 国际化 输出 令牌机制 拦截器 表单校验(jQuery.validate验证框架) 上传 下载 OGNL(ActionContext StackContext ValueStack区别)

    栈的简单代码

    值栈(ValueStack)是Struts2的一个核心概念,类似于正常的栈,符合后进先出的 栈的特点,可以在值栈中放入、删除和查询对象。Strtus2对OGNL进行了扩充,将值栈作为OGNL的根对象。 什么是值栈?符合先进后出的特点,能给...

    struts.ppt

    ognl 对象图导航语言 struts2框架默认 优点: 1.支持对象方法的调用 2.支持类的静态方法调用和常量值的调用 3.操作集合对象 &lt;s:property value="#"&gt;&lt;/s:property&gt; 除了ValueStack不加#,直接取用,其他都要加#,...

    XWork绕过安全限制执行任意命令漏洞补丁

    Struts2中WebWork框架使用XWork基于HTTP参数名执行操作和调用,将每个HTTP参数名处理为OGNL(对象图形导航语言)语句,而OGNL将: user.address.city=Bishkek&user['favoriteDrink']=kumys 转换为: action....

Global site tag (gtag.js) - Google Analytics