`
downpour
  • 浏览: 717498 次
  • 性别: Icon_minigender_1
  • 来自: 上海
博客专栏
00a07ae5-264f-3774-8903-8ad88ce08cb0
Struts2技术内幕
浏览量:117952
4d8508f2-c0dd-3df8-9483-04cc612afbbc
SpringMVC深度探险...
浏览量:230647
社区版块
存档分类
最新评论

忘记李刚,一步一步跟我学Struts2 —— 在Struts2中使用OGNL

阅读更多
专栏地址:http://www.iteye.com/wiki/struts2/1356-how-to-use-ognl-in-struts2

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

从例子开始

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

/**
 * @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的加强

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

// "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表达式将返回什么?

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属性,怎么办呢?你可以通过下面的方法:

[0].name   // call to animal.getName()
[1].name   // call to person.getName()


Struts2中的OGNL上下文环境

有了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进行计算

取值计算

有了上面的这些知识,我们就能非常容易的理解在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为什么要做出这样的设置,从源码上看,它似乎没有什么特别额外的作用:


        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进行装饰。让我们来看一下源码:

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进行设值计算的详细分析,将会在拦截器章节具体给出。


  • 描述: Struts2 OGNL context
  • 大小: 5.3 KB
38
1
分享到:
评论
12 楼 pxs623 2011-08-04  
博主写的很好,学习学习
11 楼 43385607 2009-04-28  
不错哦、顶下
10 楼 deepsea0001 2009-03-08  
好文不顶没良心
9 楼 downpour 2009-01-20  
fyting 写道

哦,我的意思是说你这里可以提醒一下其他人,不是说你这样没写对,呵呵。很多人会犯这个错,特别是在struts的form tag里,就按照jstl里的习惯写上了。并且在2.0.9里面没有问题,当版本一升级,就出现问题了。


OK,我加一下。
8 楼 fyting 2009-01-20  
哦,我的意思是说你这里可以提醒一下其他人,不是说你这样没写对,呵呵。很多人会犯这个错,特别是在struts的form tag里,就按照jstl里的习惯写上了。并且在2.0.9里面没有问题,当版本一升级,就出现问题了。
7 楼 downpour 2009-01-19  
fyting 写道

引用例如:${user.name}, &lt;c:out value="${department.name}" /&gt;
Hi downpour,
我觉得这里有必要提一下,在struts2-2.0.11以后,它自己的tag里已经不支持el了。比如:
&lt;s:hidden name="department" value="${department.name}"/&gt;会报错。


struts自己的tag是不用el的啊。我这里写的是JSTL。

struts自己的tag我不是列了另外一个例子:<s:property value="user.name" />



6 楼 fyting 2009-01-19  
引用
例如:${user.name}, <c:out value="${department.name}" />

Hi downpour,
我觉得这里有必要提一下,在struts2-2.0.11以后,它自己的tag里已经不支持el了。比如:
<s:hidden name="department" value="${department.name}"/>会报错。
5 楼 zouzaibianyuan 2009-01-08  
downpour  谢谢
4 楼 carpenter 2009-01-07  
downpour 是好人!
3 楼 flyfan 2009-01-06  
非常的不错,解决了我的一些疑问
2 楼 lyhngu 2009-01-06  
受益非浅,感谢!!!
1 楼 rain2005 2009-01-05  
downpour真是个好人,文章结构组织的很好,很适合我们这种熟悉struts1的同学来阅读,也相对期待下篇参数拦截器的文章。

相关推荐

    struts2中的OGNL的源码

    其中,OGNL(Object-Graph Navigation Language)是Struts2中的核心表达语言,用于在视图层与模型层之间传递数据。在深入理解OGNL的源码之前,我们首先需要了解OGNL的基本概念和用法。 OGNL是一种强大的表达式语言...

    struts2——docs部分

    OGNL是Struts2中的表达式语言,用于在Action和JSP之间传递数据。它允许直接访问对象的属性,以及进行各种操作,如算术运算、逻辑判断等。 **6. 自定义标签** Struts2提供了丰富的自定义标签库,简化了视图层的开发...

    struts框架——ognl源代码

    7. **OGNL与Struts2的结合**:在Struts2中,Action类的属性通过OGNL与JSP页面进行数据交换。Action的结果可以通过OGNL表达式动态决定,例如,根据某个条件跳转到不同的页面。 8. **OGNL表达式在模板中的使用**:在...

    struts2 中 OGNL表达式的使用

    struts2 中 OGNL表达式的使用struts2 中 OGNL表达式的使用

    struts2 ognl的用法

    在Struts2中使用OGNL表达式主要涉及以下几种符号:“#”、“%”和“$”。 ##### 1. “#”符号的使用 - **访问Struts2上下文**: - `#`可用于访问当前的`ActionContext`,如`#ActionContext.getContext()`。 - ...

    Struts2_OGNL

    OGNL(Object-Graph Navigation Language)是Struts2框架中的一个重要组件,用于在Web应用中访问和操作Java对象的属性,调用其方法,以及执行类型转换等操作。 OGNL是对象图导航语言的缩写,它是一种功能强大的...

    struts2 ognl用法项目

    这个项目对于初学者来说是一个很好的起点,因为它提供了实际的代码示例,帮助理解如何在实际项目中使用OGNL。通过查看源代码,你可以看到OGNL如何与Struts2的其他组件协同工作,如Action、Interceptor和Result。 ...

    Struts2 使用OGNL表达式

    这篇博客文章可能详细探讨了如何在Struts2中使用OGNL来传递和操作数据。 OGNL是Struts2的核心组件之一,它允许开发者通过简单的字符串表达式来访问和修改对象的属性,甚至可以处理复杂的对象结构。以下是一些关于...

    struts2——jar包

    Struts2是一个基于MVC(Model-View-Controller)设计模式的Java Web应用程序框架,它在Web开发领域中被广泛使用。Struts2的核心在于提供一个强大的、灵活的和可扩展的架构,使得开发者能够更方便地构建和管理业务...

    学习OGNL在Struts2中工作原理

    下面通过几个具体的例子来说明如何在Struts2中使用OGNL: 1. **设置和获取数据**: 在Action中设置值: ```java ActionContext.getContext().getValueStack().set("test", "lovejava"); ``` 在JSP页面中获取...

    Struts2核心包ognl-2的源代码

    Struts2将ActionContext与OGNL结合,使得请求参数可以直接在表达式中使用。 5. **安全特性**:在Struts2中,OGNL的不当使用可能导致安全漏洞,如著名的Struts2 OGNL注入攻击。源代码中包含了对这种风险的防护措施,...

    Struts2 技术内幕——深入解析Struts2架构设计与实现原理

    - OGNL(Object-Graph Navigation Language)如何用于表达式语言和在Struts2中的应用。 - I18N(国际化)和L10N(本地化)是如何在Struts2中实现的。 - 文件上传和下载机制。 - Struts2的安全特性,包括如何防止常见...

    struts2 技术内幕——深入解析struts2架构设计

    核心技术篇首先分析了Struts2中多种具有代表性的设计模式,然后对Struts2中的精华——OGNL表达式引擎和XWork框架的原理及机制进行了全面深入的分析和讲解。运行主线篇首先对Struts2的两大运行主线——初始化主线和...

    struts2_OGNL Demo

    这个项目旨在帮助开发者理解和学习如何在Struts2中使用OGNL来操纵对象和数据。OGNL是Struts2中一个重要的组件,它允许在视图层和控制层之间传递和操作数据。 首先,我们来看`.classpath`、`.mymetadata`、`.project...

    Struts2之Ognl详解案例TextOgnl

    因此,在实际应用中,应确保对用户输入进行严格的校验和过滤,或者使用Struts2的安全插件来防止OGNL注入。 五、总结 OGNL在Struts2中扮演着关键角色,它简化了数据绑定和对象交互。然而,开发者也需要注意其潜在的...

    Ognl在Struts2中的应用 源码

    在**Struts2**框架中,Ognl是核心组件之一,负责处理视图到模型数据的绑定,以及动作方法的调用。通过Ognl,开发者可以轻松地在JSP页面中访问Action类的属性,实现动态控制流。 **1. OGNL的基本语法** OGNL表达式...

    struts2 标签 OGNL

    在Struts2中,OGNL被广泛用于访问Action中的属性,以及在视图层(如JSP)中绑定和操作数据。 首先,OGNL使得访问对象属性变得非常直观。它支持点符号(.)和方括号([''])两种方式来访问属性。例如,如果在Action...

    Struts2 ognl

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

    struts2 OGNL表达式

    2. **Struts2动作和结果**:如何在配置文件中使用OGNL定义Action和结果。 3. **JSP标签库**:Struts2提供的JSP标签库,如`s:property`、`s:iterator`等,它们内部使用了OGNL。 4. **安全问题**:理解OGNL注入的风险...

Global site tag (gtag.js) - Google Analytics