Struts2中的OGNL详解

OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能。

OGNL表达式实现原理

Struts 2中的OGNL Context实现者为ActionContext,它结构示意图如下:

ognlcontext

当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session,另外OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈) 。如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。

在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。

ognlvalue

 

在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。 大家注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value=”name”/>
由于ValueStack(值栈)是Struts 2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性: ${foo} //获得值栈中某个对象的foo属性。如果访问其他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顺序访问其属性。

 

OGNL中重要的3个符号:#、%、$:

#、%和$符号在OGNL表达式中经常出现,而这三种符号也是开发者不容易掌握和理解的部分,需要时间的积累才渐渐弄清楚……

1.#符号

#符号的用途一般有三种。

—    访问非根对象属性,例如#session.msg表达式,由于Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀。实际上,#相当于ActionContext. getContext();#session.msg表达式相当于ActionContext.getContext().getSession(). getAttribute(“msg”) 。

—    用于过滤和投影(projecting)集合,如persons.{?#this.age>25},persons.{?#this.name==’pla1′}.{age}[0]。

—    用来构造Map,例如示例中的#{‘foo1′:’bar1’, ‘foo2′:’bar2’}。

2.%符号

%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值。如下面的代码所示:

<h3>构造Map</h3>

<s:set name=”foobar” value=”#{‘foo1′:’bar1’, ‘foo2′:’bar2’}” />

<p>The value of key “foo1″ is <s:property value=”#foobar[‘foo1’]” /></p>

<p>不使用%:<s:url value=”#foobar[‘foo1’]” /></p>

<p>使用%:<s:url value=”%{#foobar[‘foo1’]}” /></p>

运行界面如下所示。

he value of key “foo1” is bar1

不使用%:#foobar[‘foo1’]

使用%:bar1

3.$符号

$符号主要有两个方面的用途。

—    在国际化资源文件中,引用OGNL表达式,例如国际化资源文件中的代码:reg.agerange=国际化资源信息:年龄必须在${min}同${max}之间。

—    在Struts 2框架的配置文件中引用OGNL表达式,例如:

1
2
3
4
5
6
7
8
9
<validators>    
    <field name="intb">    
            <field-validator type="int">    
            <param name="min">10</param>    
            <param name="max">100</param>    
            <message>BAction-test校验:数字必须为${min}为${max}之间!</message>    
        </field-validator>    
    </field>    
</validators>

下面通过代码对OGNL有更深的了解:

action类OgnlAction.java:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.lpq.test.action;  
 
import java.util.Date;  
import java.util.LinkedList;  
import java.util.List;  
 
import javax.servlet.http.HttpServletRequest;  
 
import org.apache.struts2.ServletActionContext;  
import org.apache.struts2.convention.annotation.Action;  
import org.apache.struts2.convention.annotation.Namespace;  
import org.apache.struts2.convention.annotation.ParentPackage;  
import org.apache.struts2.convention.annotation.Result;  
import org.apache.struts2.convention.annotation.Results;  
import org.springframework.stereotype.Controller;  
 
import com.opensymphony.xwork2.ActionContext;  
import com.opensymphony.xwork2.ActionSupport;  
 
@Controller  
@Namespace("/test")  
@ParentPackage("struts-default")  
@Results( { @Result(name = "success", location = "/other_test/showognl.jsp"),  
        @Result(name = "fail", location = "/bbs/admin_login.jsp"),  
        @Result(name = "input", location = "/bbs/admin_login.jsp") })  
public class OgnlAction extends ActionSupport {  
    private static final long serialVersionUID = -1494290883433357310L;  
    private List<Person> persons;  
 
    @Action("ognlTest")  
    public String ognlTest() throws Exception {  
        // 获得ActionContext实例,以便访问Servlet API  
        ActionContext ctx = ActionContext.getContext();  
        // 存入application  
        ctx.getApplication().put("msg", "application信息");  
        // 保存session  
        ctx.getSession().put("msg", "seesion信息");  
        // 保存request信息  
        HttpServletRequest request = ServletActionContext.getRequest();  
        request.setAttribute("msg", "request信息");  
        // 为persons赋值  
        persons = new LinkedList<Person>();  
        Person person1 = new Person();  
        person1.setName("pla1");  
        person1.setAge(26);  
        person1.setBirthday(new Date());  
        persons.add(person1);  
 
        Person person2 = new Person();  
        person2.setName("pla2");  
        person2.setAge(36);  
        person2.setBirthday(new Date());  
        persons.add(person2);  
 
        Person person3 = new Person();  
        person3.setName("pla3");  
        person3.setAge(16);  
        person3.setBirthday(new Date());  
        persons.add(person3);  
 
        return SUCCESS;  
 
    }  
 
    public List<Person> getPersons() {  
        return persons;  
    }  
 
    public void setPersons(List<Person> persons) {  
        this.persons = persons;  
    }  
}

jsp页面showognl.jsp:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>  
 
<%@ taglib prefix="s" uri="/struts-tags" %>  
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/ xhtml1/DTD/xhtml1-transitional.dtd">  
 
<html xmlns="http://www.w3.org/1999/xhtml">  
 
<head>  
 
    <title>Struts2 OGNL 演示</title>  
 
</head>  
 
<body>      
 
    <h3>访问OGNL上下文和Action上下文</h3>  
 
    <!-使用OGNL访问属性值-->  
 
    <p>parameters: <s:property value="#parameters.msg" /></p>  
 
    <p>request.msg: <s:property value="#request.msg" /></p>  
 
    <p>session.msg: <s:property value="#session.msg" /></p>  
 
    <p>application.msg: <s:property value="#application.msg" /></p>  
 
    <p>attr.msg: <s:property value="#attr.msg" /></p>  
 
    <hr />  
 
    <h3>用于过滤和投影(projecting)集合</h3>  
 
    <p>年龄大于20</p>  
 
    <ul>  
 
    <!-判断年龄-->  
 
        <s:iterator value="persons.{?#this.age>20}">  
 
            <li><s:property value="name" /> - 年龄:<s:property value="age" /></li>  
 
        </s:iterator>  
 
    </ul>  
 
    <p>姓名为pla1的年龄: <s:property value="persons.{?#this.name=='pla1'}.{age}[0]"/></p>  
 
    <hr />  
 
    <h3>构造Map</h3>  
 
    <s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" />  
 
    <p>The value of key "foo1" is <s:property value="#foobar['foo1']" /></p>  
   
    <hr />
   
    <h4>%符号的用法</h4>  
 
    <s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" />  
 
    <p>The value of key "foo1" is <s:property value="#foobar['foo1']" /></p>  
 
    <p>不使用%:<s:url value="#foobar['foo1']" /></p>  
 
    <p>使用%:<s:url value="%{#foobar['foo1']}" /></p>  
 
    <hr />
        <%  
           request.setAttribute("req", "request scope");  
           request.getSession().setAttribute("sess", "session scope");  
           request.getSession().getServletContext().setAttribute("app",  
                   "aplication scope");  
       %>  
        1.通过ognl表达式获取 属性范围中的值  
        <br>  
        <s:property value="#request.req" />  
        <br />  
        <s:property value="#session.sess" />  
        <br />  
        <s:property value="#application.app" />  
        <br />  
        <hr>  
 
       2.通过<span style="background-color: #fafafa;">ognl表达式创建list 集合 ,并且遍历出集合中的值  
        <br>  
        <s:set name="list" value="{'eeeee','ddddd','ccccc','bbbbb','aaaaa'}"></s:set>  
        <s:iterator value="#list" var="o">  
            <!-- ${o }<br/> -->  
            <s:property />  
            <br />  
        </s:iterator>  
        <br />  
        <hr>  
 
       3.通过ognl表达式创建Map 集合 ,并且遍历出集合中的值  
        <br>  
        <s:set name="map"  
           value="#{'1':'eeeee','2':'ddddd','3':'ccccc','4':'bbbbb','5':'aaaaa'}"></s:set>  
        <s:iterator value="#map" var="o">  
            <!--      ${o.key }->${o.value }<br/>   -->  
            <!-- <s:property value="#o.key"/>-><s:property value="#o.value"/><br/>   -->  
            <s:property value="key" />-><s:property value="value" />  
            <br />  
        </s:iterator>  
        <br />  
        <hr>  
      4.通过ognl表达式 进行逻辑判断  
        <br>  
        <s:if test="'aa' in {'aaa','bbb'}">  
            aa 在 集合{'aaa','bbb'}中;  
        </s:if>  
        <s:else>  
            aa 不在 集合{'aaa','bbb'}中;  
        </s:else>  
        <br />  
        <s:if test="#request.req not in #list">  
                不 在 集合list中;  
        </s:if>  
        <s:else>  
             在 集合list中;  
        </s:else>  
        <br />  
        <hr>  
       
       5.通过ognl表达式 的投影功能进行数据筛选  
        <br>  
        <s:set name="list1" value="{1,2,3,4,5}"></s:set>  
        <s:iterator value="#list1.{?#this>2}" var="o">  
            <!-- #list.{?#this>2}:在list1集合迭代的时候,从中筛选出当前迭代对象>2的集合进行显示 -->  
            ${o }<br />  
        </s:iterator>  
        <br />  
        <hr>  
       6.通过ognl表达式 访问某个类的静态方法和值  
        <br>  
        <s:property value="@java.lang.Math@floor(32.56)" />  
 
        <s:property value="@com.rao.struts2.action.OGNL1Action@aa" />  
        <br />  
        <br />  
        <hr>  
      7.ognl表达式 迭代标签 详细  
        <br>  
        <s:set name="list2"  
           value="{'aa','bb','cc','dd','ee','ff','gg','hh','ii','jj'}"></s:set>  
        <table border="1">  
            <tr>  
                <td>索引 </td>  
                <td></td>  
                <td>奇?</td>  
                <td> 偶?</td>  
                <td>首?</td>  
                <td> 尾?</td>  
                <td>当前迭代数量</td>  
            </tr>  
            <s:iterator value="#list2" var="o" status="s">  
                <tr bgcolor="<s:if test="#s.even">pink</s:if>">  
                    <td>  
                        <s:property value="#s.getIndex()" />  
                    </td>  
                    <td>  
                        <s:property />  
                    </td>  
                    <td>  
                        <s:if test="#s.odd">Y</s:if>  
                        <s:else>N</s:else>  
                    </td>  
                    <td>  
                        <s:if test="#s.even">Y</s:if>  
                        <s:else>N</s:else>  
                    </td>  
                    <td>  
                        <s:if test="#s.first">Y</s:if>  
                        <s:else>N</s:else>  
                    </td>  
                    <td>  
                        <s:if test="#s.isLast()">Y</s:if>  
                        <s:else>N</s:else>  
                    </td>  
                    <td>  
                    <s:property value="#s.getCount()"/>  
                </td>  
                </tr>  
            </s:iterator>  
        </table>  
        <br>  
        <hr>      
       
       
       8.ognl表达式:  if/else if/else 详细<br>  
        <% request.setAttribute("aa",0); %>  
        <s:if test="#request.aa>=0 && #request.aa<=4">  
                在0-4之间;  
       </s:if>  
       <s:elseif test="#request.aa>=4 && #request.aa<=8">  
                在4-8之间;  
       </s:elseif>  
       <s:else>  
             大于8;  
       </s:else>  
       <br>  
       <hr>  
   9.ognl表达式: url 详细<br>  
       <% request.setAttribute("aa","sss"); %>  
        <s:url action="testAction" namespace="/aa/bb">  
            <s:param name="aa" value="#request.aa"></s:param>  
            <s:param name="id">100</s:param>  
        </s:url>  
        <br/>  
        <s:set name="myurl" value="'http://www.baidu.com'"></s:set>  
        value以字符处理:   <s:url value="#myurl"></s:url><br>  
        value明确指定以ognl表达式处理:    <s:url value="%{#myurl}"></s:url>  
        <br>  
        <hr>  
    10.ognl表达式: checkboxlist 详细<br>  
        1> .list 生成;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br>  
        name:checkboxlist的名字<br>  
        list:checkboxlist要显示的列表<br>  
        value:checkboxlist默认被选中的选项,checked=checked<br>  
        <s:checkboxlist name="checkbox1" list="{'上网','看书','爬山','游泳','唱歌'}" value="{'上网','看书'}" ></s:checkboxlist>  
        <br>  
         以上生成代码:<br>  
        <xmp>  
            <input type="checkbox" name="checkbox1" value="上网" id="checkbox1-1" checked="checked"/>  
            <label for="checkbox1-1" class="checkboxLabel">上网</label>  
            <input type="checkbox" name="checkbox1" value="看书" id="checkbox1-2" checked="checked"/>  
            <label for="checkbox1-2" class="checkboxLabel">看书</label>  
            <input type="checkbox" name="checkbox1" value="爬山" id="checkbox1-3"/>  
            <label for="checkbox1-3" class="checkboxLabel">爬山</label>  
            <input type="checkbox" name="checkbox1" value="游泳" id="checkbox1-4"/>  
            <label for="checkbox1-4" class="checkboxLabel">游泳</label>  
            <input type="checkbox" name="checkbox1" value="唱歌" id="checkbox1-5"/>  
            <label for="checkbox1-5" class="checkboxLabel">唱歌</label>"  
        </xmp>  
        2> .Map 生成;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br>  
        name:checkboxlist的名字<br>  
        list:checkboxlist要显示的列表<br>  
        listKey:checkbox 的value的值<br>  
        listValue:checkbox 的lablel(显示的值)<br>  
        value:checkboxlist默认被选中的选项,checked=checked<br>  
        <s:checkboxlist name="checkbox2" list="#{1:'上网',2:'看书',3:'爬山',4:'游泳',5:'唱歌'}" listKey="key" listValue="value" value="{1,2,5}" ></s:checkboxlist>  
        <br>  
                       以上生成代码:<br>  
        <xmp>  
            <input type="checkbox" name="checkbox2" value="1" id="checkbox2-1" checked="checked"/>  
            <label for="checkbox2-1" class="checkboxLabel">上网</label>  
            <input type="checkbox" name="checkbox2" value="2" id="checkbox2-2" checked="checked"/>  
            <label for="checkbox2-2" class="checkboxLabel">看书</label>  
            <input type="checkbox" name="checkbox2" value="3" id="checkbox2-3"/>  
            <label for="checkbox2-3" class="checkboxLabel">爬山</label>  
            <input type="checkbox" name="checkbox2" value="4" id="checkbox2-4"/>  
            <label for="checkbox2-4" class="checkboxLabel">游泳</label>  
            <input type="checkbox" name="checkbox2" value="5" id="checkbox2-5" checked="checked"/>  
            <label for="checkbox2-5" class="checkboxLabel">唱歌</label>  
        </xmp>  
        <hr>  
</body>  
</html>

总结OGNL的使用方法:

访问属性

名字属性获取:<s:property value=”user.username”/><br>

地址属性获取:<s:property value=”user.address.addr”/><br>

访问方法

调用值栈中对象的普通方法:<s:property value=”user.get()”/><br>

访问静态属性和方法

调用Action中的静态方法:<s:property value=”@struts.action.LoginAction@get()”/>

调用JDK中的类的静态方法:<s:property value=”@java.lang.Math@floor(44.56)”/><br>

调用JDK中的类的静态方法(同上):<s:property value=”@@floor(44.56)”/><br>

调用JDK中的类的静态方法:<s:property value=”@java.util.Calendar@getInstance()”/><br>

调用普通类中的静态属性:<s:property value=”@struts.vo.Address@TIPS”/><br>

访问构造方法

调用普通类的构造方法:<s:property value=”new struts.vo.Student(‘李晓红’ , ‘美女’ , 3 , 25).username”/>

1.5. 访问数组

获取List:<s:property value=”testList”/><br>

获取List中的某一个元素(可以使用类似于数组中的下标获取List中的内容):

<s:property value=”testList[0]”/><br>

获取Set:<s:property value=”testSet”/><br>

获取Set中的某一个元素(Set由于没有顺序,所以不能使用下标获取数据):

<s:property value=”testSet[0]”/><br> ×

获取Map:<s:property value=”testMap”/><br>

获取Map中所有的键:<s:property value=”testMap.keys”/><br>

获取Map中所有的值:<s:property value=”testMap.values”/><br>

获取Map中的某一个元素(可以使用类似于数组中的下标获取List中的内容):

<s:property value=”testMap[‘m1’]”/><br>

获取List的大小:<s:property value=”testSet.size”/><br>

访问集合 – 投影、选择(? ^ $)

利用选择获取List中成绩及格的对象:<s:property value=”stus.{?#this.grade>=60}”/><br>

利用选择获取List中成绩及格的对象的username:

<s:property value=”stus.{?#this.grade>=60}.{username}”/><br>

利用选择获取List中成绩及格的第一个对象的username:

<s:property value=”stus.{?#this.grade>=60}.{username}[0]”/><br>

利用选择获取List中成绩及格的第一个对象的username:

<s:property value=”stus.{^#this.grade>=60}.{username}”/><br>

利用选择获取List中成绩及格的最后一个对象的username:

<s:property value=”stus.{$#this.grade>=60}.{username}”/><br>

利用选择获取List中成绩及格的第一个对象然后求大小:

<s:property value=”stus.{^#this.grade>=600}.{username}.size”/><br>

集合的伪属性

OGNL能够引用集合的一些特殊的属性,这些属性并不是JavaBeans模式,例如size(),length()等等. 当表达式引用这些属性时,OGNL会调用相应的方法,这就是伪属性.

ognl2

Lambda   :[…]

格式::[…]

使用Lambda表达式计算阶乘:

<s:property value=”#f = :[#this==1?1:#this*#f(#this-1)] , #f(4)”/><br>

OGNL中#的使用

#可以取出堆栈上下文中的存放的对象.

ognl3

OGNL中%的使用

用%{}可以取出存在值堆栈中的Action对象,直接调用它的方法.

例如你的Action如果继承了ActionSupport .那么在页面标签中,用%{getText(‘key’)}的方式可以拿出国际化信息.

OGNL中$的使用

“$”有两个主要的用途

l         用于在国际化资源文件中,引用OGNL表达式

l         在Struts 2配置文件中,引用OGNL表达式

值栈

ValueStack对象。这个对象贯穿整个Action的生命周期(每个Action类的对象实例会拥有一个ValueStack对象)。当 Struts 2接收到一个.action的请求后,会先建立Action类的对象实例,但并不会调用Action方法,而是先将Action类的相应属性放到 ValueStack对象的顶层节点(ValueStack对象相当于一个栈)。

在Action中获得ValueStack对象:ActionContext.getContext().getValueStack()

l         Top语法

使用Top获取值栈中的第二个对象:<s:property value=”[1].top.对象”/>

l         N语法

使用N获取值栈中的第二个对象:<s:property value=”[1].对象”/>

l         @语法

调用action中的静态方法:<s:property value=”@vs1@静态方法”/> vs:值栈 1:表示第一个。

 

部分转载自网络。

本文地址:http://www.alonemonkey.com/struts2-ognl.html

本文链接:http://www.alonemonkey.com/struts2-ognl.html