`

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

    Struts2 ognl

    Struts2 OGNL(Object-Graph Navigation Language)是一种强大的表达式语言,它在Struts2框架中扮演着核心角色,用于数据绑定、控制流程以及动态方法调用。这篇博文可能详细介绍了Struts2框架中OGNL的使用、工作原理...

    struts2 ognl源码

    - `ValueStack`是Struts2的核心组件,它存储了Action上下文中的所有对象,OGNL通过这个栈来查找和操作对象。 2. **值查找与设置**: - OGNL使用`ValueAccessor`接口来访问对象的属性。不同的类型(如POJO、Map等...

    ognl.rar_OGNL API_struts2 ognl api_struts2帮助文档

    5. **OGNL与Struts2的集成**:Struts2是如何将OGNL与Action、ValueStack和结果视图模板结合的。 6. **OGNL的异常处理**:遇到错误时,如何理解和解决OGNL表达式抛出的异常。 7. **最佳实践**:如何优化OGNL的使用...

    很全面的struts2_ognl总结

    OGNL(Object-Graph Navigation Language)是一种基于 Java 的表达式语言,Struts2 框架中使用 OGNL 来访问和操作 ValueStack 中的对象。在 Struts2 中,OGNL 是一个强大的工具,允许开发者访问和操作 ValueStack 中...

    struts2 ognl

    8. **Struts2中的OGNL拦截器**:Struts2框架使用了一系列拦截器来增强OGNL的功能,例如ValueStack,它可以将Action上下文中的所有对象暴露给OGNL表达式。这使得在视图层直接访问Action的属性变得简单。 总的来说,...

    struts2OGNL表达式ActionContext及valuesStack.pdf

    Struts2的OGNL上下文进一步扩展了这个概念,它包括了一个名为`ValueStack`的组件。`ValueStack`是一个对象栈,用于管理多个对象,这些对象可以被依次访问。当你使用`#request`、`#session`或`#attr`时,实际上是在`...

    Struts用的ognl和valueStack(vs)实例

    总的来说,OGNL和Value Stack是Struts2框架中紧密协作的两个组件,它们极大地简化了数据的传递和视图的渲染,提高了开发效率。了解并熟练掌握这两者,对于深入理解和使用Struts2框架至关重要。在学习过程中,可以...

    struts2对Ognl的封装--PropertyAccessor

    - Struts2中OGNL与Action、ValueStack的关系。 - OGNL的安全性问题,如OGNL注入攻击,以及如何防范。 在压缩包文件`struts-ognl.asta`中,可能包含了一些示例或测试用例,用于演示Struts2中PropertyAccessor的使用...

    Struts2 OGNL表达式实例详解

    在Struts2中,OGNL可以用来访问Action中的属性、方法,也可以用来访问ValueStack中的数据。在本文中,我们将通过实例代码来详细介绍OGNL的使用。 实例1:访问Action中的属性 在Struts2中,我们可以使用OGNL来访问...

    struts2对Ognl的封装--TypeConverter

    这篇博客文章"Struts2对Ognl的封装--TypeConverter"探讨了Struts2如何通过TypeConverter机制来增强OGNL的功能。 首先,我们来看一下OGNL。OGNL允许开发者用简洁的语法来获取和设置对象的属性,甚至可以进行复杂的...

    struts2中OGNL的一些概念及原理说明

    ### Struts2中OGNL的概念及原理说明 #### 一、OGNL表达式的基本用法与特点 **OGNL**,即**Object Graphic Navigation Language**(对象图导航语言),是Struts2框架中用于处理对象及其属性的强大工具之一。通过...

    Struts2 & OGNL

    OGNL(Object-Graph Navigation Language)是Struts2中的默认表达式语言,用于在视图和控制器之间传递数据。在这个主题中,我们将深入探讨Struts2与OGNL的整合以及它们在实际开发中的应用。 **Struts2框架概述** ...

    struts2中的ognl

    - 值栈(ValueStack)是Struts2处理请求的核心概念,它是一个对象栈,存放了请求处理过程中涉及的所有对象。使用OGNL时,通常配合Struts2的标签,比如`s:property`,通过值栈来访问和显示数据。 3. **EL与值栈的...

    Struts2中关于ValueStack的一些操作

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

    ognl 源码: struts2 所用的 ognl 包源码

    在 Struts2 中,这个上下文通常是 Struts2 的 ValueStack,它是一个包含了所有可访问对象的栈结构,使得可以通过简单的表达式来查找和操作数据。 6. **Struts2 结合 OGNL**: 在 Struts2 中,Action 对象的属性...

    精通struts2的ognl表达式

    - 在Struts2中,可以使用OGNL来访问ValueStack中的值。ValueStack是Struts2的核心组件之一,用于存储请求期间的数据。 - 例如,可以通过`&lt;s:property value="cat.name"/&gt;`来获取Cat对象的name属性值。 - 使用`#`...

    s20505_valueStack_OGNL

    在给定的压缩包文件“s20505_valueStack_OGNL”中,我们关注的核心知识点是Struts2中的OGNL(Object-Graph Navigation Language)和相关标签的使用。OGNL是Struts2默认的表达式语言,用于在视图层和控制器层之间传递...

    struts2.0中的ognl

    在Struts2中,OGNL(Object-Graph Navigation Language)是一种强大的表达式语言,用于获取和设置对象属性,它在Struts2的核心组件中扮演着至关重要的角色。本篇文章将深入探讨OGNL在Struts2.0中的应用及其重要性。 ...

    Struts2的标签库及OGNL表达式语言

    Struts2是一个流行的Java web开发框架,其核心特性之一就是使用OGNL(Object-Graph Navigation Language)作为表达式语言,并提供了丰富的标签库来简化视图层的开发。本篇文章将详细探讨Struts2中的OGNL表达式语言...

Global site tag (gtag.js) - Google Analytics