OGNL表达式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OGNL表达式相关的知识,希望对你有一定的参考价值。

一.知识点学习

1.struts2中包含以下6种对象,requestMap,sessionMap,applicationMap,paramtersMap,attr,valueStack;

 技术分享

1)requestMap用来存放包含当前HttpServletRequest的属性(attribute)的Map,简单来说就是request域中的值;   

2)sessionMap用来存放包含当前HttpSession的属性(attribute)的Map

3)applicationMap用来存放包含当前应用的ServletContext的属性(attribute)的Map

4)paramtersMap包含当前HTTP请求参数的Map 

5)attr,只是用来取值,用于按request > session > application顺序访问其属性(attribute)

6)valueStack值栈是Action的数据中心,关于Action中的所有Value都是存放在valueStack中.

2.OGNL几种常见的符号用法

<s:property value="#attr.username"/>会在以下几个域对象中依次查询

[pageContext对象]===>requestMap对象===>valueStack对象===>sessionMap对象===>applicationMap对象===>空白字符串

注意:pageContext对象不是Struts2的数据中心之一.

3.#用法:

1) <s:property value="#request.username"/> 作用于struts2的域对象,而不是普通域对象

2)<s:property value="#user.username"/>作用于JavaBean对象

3)<s:property value="#username"/><===><s:property value="username"/>作用于普通字符串

  OGNL (Object Graph Navigation Language)对象图导航语言。Struts2框架使用OGNL作为默认的表达式语言。 
  OGNL相对其它表达式语言具有下面几大优势:

  • 支持对象方法调用,如xxx.doSomeSpecial();
  • 支持类静态方法的调用和值的访问,表达式的格式: 
      @[类全名(包括包路径)]@[方法名 | 值名],例如: 
      @[email protected](‘foo %s’, ‘bar’) 
      或@[email protected]_NAME;
  • 支持赋值操作和表达式串联;
  • 访问OGNL上下文ActionContext;
  • 操作集合对象List,Map。
      Ognl 有一个上下文(Context)概念,在 Struts2 中上下文(Context)的实现为ActionContext,ActionContext对象对应Action,可以理解为action执行相关的对象的容器,包括application,session,parameters,locale,value stack等。ActionContext中:

  • ValueStack是OGNL上下文的root元素,贯穿整个 Action 的生命周期(每个Action类的对象实例都拥有一个ValueStack 对象)。 相当于一个数据的中转站. 在其中保存当前Action 对象和其他相关对象。

  • private Map<String, Object> context;这个才是真正意义上的上下文。其中可以put进数据,jsp中可通过OGNL表达式通过key获取。 
      OGNL Context 实现者为 ActionContext ,它结构示意图如下 : 
    技术分享 

      注:这里的context map指的是ActionContext的map效果;action为相关的action包名,而不是action对象(代码中更清晰)。

16.2 理解Struts2中的 ValueStack和OGNL Context

  ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。 
  ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前Action 对象和其他相关对象。 
  OgnlValueStack 类包含两个重要的属性:

    CompoundRoot root; // CompoundRoot extends ArrayList
    transient Map<String, Object> context;
  • 1
  • 2
  • 其中root为一个ArrayList模拟的值栈,action等相关对象就是保存这里:
    /**
     * 实现ValueStack接口的pop操作
     * @see com.opensymphony.xwork2.util.ValueStack#pop()
     */
    public Object pop() {
        return root.pop();
    }

    /**
     * 实现ValueStack接口的push操作
     * @see com.opensymphony.xwork2.util.ValueStack#push(java.lang.Object)
     */
    public void push(Object o) {
        root.push(o);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

  再查看ActionContext类的源代码可知:

public class ActionContext implements Serializable {

    static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();

    // 所要执行的action类名
    public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";

    // 值栈ValueStack的类名,即上面所说的_root对象
    public static final String VALUE_STACK = ValueStack.VALUE_STACK;
    public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
    public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
    public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
    public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
    // context map
    private Map<String, Object> context;
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  会发现ActionContext中包含一个context,OgnlValueStack中也包含一个context,这两个context是否为同一个对象呢??我们在action的execute方法中进行测试:

    public String execute() {
        ActionContext context = ActionContext.getContext();
        OgnlValueStack ognlValueStack = (OgnlValueStack) context.getValueStack();
        // 获取OgnlValueStack中的context属性
        Map<String, Object> ognlValueStack_Context = ognlValueStack.getContext();
        // 获取ActionContext中的context属性
        Map<String, Object> actionContext_Context = context.getContextMap();
        // 比较ActionContext中的context属性和OgnlValueStack中的context属性是否为同一个对象
        System.out.println(ognlValueStack_Context == actionContext_Context); // true

        return SUCCESS;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  测试结果为true!所以说ActionContext中的context属性和OgnlValueStack中的context属性实则为同一个对象!这也就解释了:通过ActionCOntext.getContext().put();方法放入到context(Map集合)的数据,也即OgnlValueStack中context(Map集合),所以可以通过OGNL表达式可以获取。 
  :在Ognl上下文对象(ActionContext)中,维持值栈root的包名(com.opensymphony.xwork2.util.ValueStack.ValueStack),OGNL访问root中的元素时,是不需要#号的,直接通过元素的名称来进行访问;而访问其他对象时,如application、request、session、parameters、attr等,则需要#号引用。

  当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action等。然后把action存放进值栈OgnlValueStack的root对象中,所以在页面上通过Struts2标签访问Action的属性时,就不需要通过#号来引用。

  由于 ValueStack( 值栈 ) 是 Struts 2 中 OGNL上下文的根对象,如果用户需要访问值栈中的对象,在 JSP 页面可以通过EL表达式访问 ValueStack(值栈)中对象的属性:

${foo} // 获得值栈中某个对象的 foo 属性
  • 1

  如果访问其他 Context 中的对象,由于他们不是根对象,所以在访问时,需要添加 # 前缀。

  • application对象:用于访问ServletContext,例如#application.userName或者#application[‘userName’],相当于调用ServletContext的getAttribute(“username”)。
  • session对象:用来访问HttpSession ,例如#session.userName或者#session[‘userName’] ,相当于调用session.getAttribute(“userName”)。
  • request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request[‘userName’] ,相当于调用request.getAttribute(“userName”)。
  • parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters[‘userName’] ,相当于调用 request.getParameter(“username”)。
  • attr对象:用于按page->request->session->application顺序访问其属性。

16.3 为何使用EL 表达式能够访问valueStack中对象的属性

  在JSP页面中通过${foo} 这样的EL表达式就可以获取值栈中某个对象的 foo 属性。原因是Struts2对HttpServletRequest作了进一步的封装。简略代码如下:

public class StrutsRequestWrapper extends HttpServletRequestWrapper {
    public StrutsRequestWrapper(HttpServletRequest req) {
        super(req);
    }
    public Object getAttribute(String key) {
        ......
        ActionContext ctx = ActionContext.getContext();
        Object attribute = super.getAttribute(key);// 先从 request 范围获取属性值
        // 如果从 request 范围没有找到属性值 , 即从 ValueStack 中查找对象的属性值
        if (ctx != null && attribute == null) {
            ......
                    ValueStack stack = ctx.getValueStack();
                        if (stack != null) {
                             attribute = stack.findValue(key);
                        }
            ......
        }
        return attribute;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

16.4 基本案例测试

  案例需求:输入界面通过表单输入数据,并注入到action中,输出界面通过Ognl表达式获取输入的内容中未保存到action部分和保存的部分,已经action中只提供get方法的属性。(即获取以上讨论的三种类型的数据) 
  (1)输入界面input.jsp

<form action="/Ognl/day07/ognlTest.action" method="post">
    username: <input type="text" name="username"/><br>
    phone:   <input type="text" name="phone"/><br>
    <!-- 此属性并没有注入到action的属性中 -->
    other:  <input type="text" name="other"/><br>
    <input type="submit" value="提交">
</form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  (2)Action类:

package com.markliu.day07;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class OgnlTestAction extends ActionSupport {
    private static final long serialVersionUID = 4491003976083155453L;

    private String username;
    private String phone;
    private String message;

    public String getMessage() {
        return message;
    }

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String execute() {
        this.message = "从action获取message!";
        return SUCCESS;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

  (3)struts.xml配置:

    <package name="day07" namespace="/day07" extends="struts-default">
            <action name="ognlTest" class="com.markliu.day07.OgnlTestAction" method="execute">
                <result name="success">/pages/day07/ognlGetInfo.jsp</result>
            </action>
    </package>
  • 1
  • 2
  • 3
  • 4
  • 5

  (4)ognl获取数据显示:

<%@ taglib uri="/struts-tags" prefix="s" %>
 ...
<body>
输入的值并保存到action中的属性:
<s:property value="username"/><br>
${phone }<br>
输入的值没有保存到action中的属性:${other }<s:property value="other"/><br>
action中只提供get方法的属性: ${message }
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  (5)运行显示:

输入的值并保存到action中的属性: wq
18212345678
输入的值没有保存到action中的属性:
action中只提供get方法的属性: action获取message! 
  • 1
  • 2
  • 3
  • 4

  由运行结果可知:由于action被保存到valuestack值栈中,所以用户输入的信息注入到action中的属性,以及action本身的属性(提供get方法)都可以通过OGNL表达式的两种形式获取:EL表达式或Struts2标签。

16.5 采用 OGNL 表达式创建 List/Map 集合对象

  如果需要一个集合元素的时候(例如 List 对象或者 Map 对象),可以使用 OGNL 中同集合相关的表达式。 
  使用如下代码直接生成一个 List 对象:

<s:set name="list" value="{‘zhangming‘,‘xiaoi‘,‘liming‘}" />
<!-- s:iterator会将当前迭代的对象放到值栈的栈顶中!因此才会有s:property默认输出ValueStack栈顶的值 -->
<s:iterator value="#list" id="n">
    <s:property value="n"/><br>
</s:iterator>
<!-- s:property标签的value属性可选,如果没有给定,则默认输出ValueStack栈顶的值
     因此还可以简写成如下形式 -->
<s:iterator value="#list">
    <s:property/><br>
</s:iterator>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  生成一个 Map 对象:

<s:set name="foobar" value="#{‘foo1‘:‘bar1‘, ‘foo2‘:‘bar2‘}" />
<s:iterator value="#foobar" >
    <!-- Map集合迭代采用了内部的Entry类,且此时迭代的Entry对象放在值栈的栈顶,
    Entry中包含key和value的属性,因此可以通过OGNL表达式获取栈顶对象的属性 -->
    <s:property value="key"/>=<s:property value="value"/><br>
</s:iterator>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  <s:set>标签用于将某个值放入指定范围。 

- scope:指定变量被放置的范围,该属性可以接受application、session、request、page或action。如果没有设置该属性,则默认放置在ActionContext(OGNL上下文)中。 
- value :赋给变量的值 . 如果没有设置该属性 , 则将 ValueStack 栈顶的值赋给变量。
 
  对于集合类型, OGNL 表达式可以使用 in 和 not in 两个元素符号。其中, in 表达式用来判断某个元素是否在指定的集合对象中; not in 判断某个元素是否不在指定的集合对象中。s:if标签的test中填入OGNL表达式,如下所示。

<s:if test="‘foo‘ in {‘foo‘,‘bar‘}">
</s:if>
<s:else>
</s:else>

<s:if test="‘foo‘ not in {‘foo‘,‘bar‘}">
</s:if>
<s:else>
</s:else>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

16.6 OGNL 表达式的投影功能

  除了 in 和 not in 之外, OGNL 还允许使用某个规则获得集合对象的子集,常用的有以下 3 个相关操作符: 
? :获得所有符合逻辑的元素。 
^ :获得符合逻辑的第一个元素。 
$ :获得符合逻辑的最后一个元素。 
例如代码,获取价格大于35的所有图书:

<s:iterator value="books.{?#this.price > 35}">
    <s:property value="title" /> - $<s:property value="price" /><br>
</s:iterator>
  • 1
  • 2
  • 3

  在上面代码中,直接在集合后紧跟 .{} 运算符表明用于取出该集合的子集, {} 内的表达式用于获取符合条件的元素, this 指的是为了从大集合 books 筛选数据到小集合,需要对大集合 books 进行迭代,this 代表当前迭代的元素。

 

几种与OGNL有关的符号

 

在Struts2中使用OGNL经常会接触到几个有关的符号:”#”,”%”,”$”。刚开始学习的时候经常分布清楚这几个符号的作用,这里我们对他们的作用大致做一个列举。

 

“#”的作用:

 

(1)   访问非root对象的属性。例如:#session[“userName”]

 

(2)   对集合进行投影与选择

 

(3)   构造对象,

 

“%”的作用:

 

在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式 <s:property value="%{#foobar[‘foo1‘]}" />

 

 “$”的作用:

 

(1)   在配置文件中引用OGNL表达式(访问Action的属性)。

 

(2)   在国际化资源文件中引用OGNL表达式(学习国际化时会学到)

 

 

 
  参考: 
http://blog.csdn.net/songylwq/article/details/5645040 
http://www.cnblogs.com/huzi007/p/4279807.html 
https://commons.apache.org/proper/commons-ognl/language-guide.html

转载请注明出处:http://blog.csdn.net/mark_lq/article/details/49839895

以上是关于OGNL表达式的主要内容,如果未能解决你的问题,请参考以下文章

Struts2中OGNL表达式的用法

什么是Ognl

ognl表达式和s标签儿

Struts2学习笔记三:OGNL表达式学习Struts2与Ognl表达式的结合原理

Ognl 和 EL 和jstl区别

OGNL与值栈