Struts2的OGNL表達式語言

xyang81 14年前發布 | 5K 次閱讀 HTML KeyValue存儲 SOA Java7

1.OGNL表達式語言
OGNL
Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目。 Struts 2框架使用OGNL作為默認的表達式語言。

相對EL表達式,它提供了平時我們需要的一些功能,如:
支持對象方法調用,如xxx.sayHello()
支持類靜態方法調用和值訪問,表達式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')@cn.itcast.Constant@APP_NAME

操作集合對象

Ognl 有一個上下文(Context)概念,說白了上下文就是一個MAP結構,它實現了java.utils.Map接口,在Struts2中上下文(Context)的實現為ActionContext,下面是上下文(Context)的結構示意圖
2.
訪問上下文(Context)中的對象需要使用#符號標注命名空間,如#application#session

另外OGNL會設定一個根對象(root對象),在Struts2中根對象就是ValueStack(值棧) 。如果要訪問根對象(即ValueStack)中對象的屬性,則可以省略#命名空間,直接訪問該對象的屬性即可。

struts2中,根對象ValueStack的實現類為OgnlValueStack,該對象不是我們想像的只存放單個值,而是存放一組對象。在OgnlValueStack類里有一個List類型的root變量,就是使用他存放一組對象
|--request  
|--application  
context ------|--OgnlValueStack root
變量[action, OgnlUtil, ... ]  
|--session  
|--attr  
|--parameters

root變量中處于第一位的對象叫棧頂對象。通常我們在OGNL表達式里直接寫上屬性的名稱即可訪問root變量里對象的屬性,搜索順序是從棧頂對象開始尋找,如果棧頂對象不存在該屬性,就會從第二個對象尋找,如果沒有找到就從第三個對象尋找,依次往下訪問,直到找到為止。
大家注意: Struts2中,OGNL表達式需要配合Struts標簽才可以使用。如:<s:property value="name"/>

3.由于ValueStack(值棧)Struts 2OGNL的根對象,如果用戶需要訪問值棧中的對象,在JSP頁面可以直接通過下面的EL表達式訪問ValueStack(值棧)中對象的屬性:
${foo} //
獲得值棧中某個對象的foo屬性

如果訪問其他Context中的對象,由于他們不是根對象,所以在訪問時,需要添加#前綴。
application
對象:用于訪問ServletContext,例如#application.userName或者#application['userName'],相當于調用ServletContextgetAttribute("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順序訪問其屬性。

4.為何使用EL表達式能夠訪問valueStack中對象的屬性

原因是Struts2HttpServletRequest作了進一步的封裝。簡略代碼如下:

public class StrutsRequestWrapper extends HttpServletRequestWrapper {
public StrutsRequestWrapper(HttpServletRequest req) {
super(req);
}
public Object getAttribute(String s) {
......
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s);//
先從request范圍獲取屬性值

if (ctx != null) {
if (attribute == null) {//
如果從request范圍沒有找到屬性值,即從ValueStack中查找對象的屬性值
......
ValueStack stack = ctx.getValueStack();
attribute = stack.findValue(s);
......
}
}
return attribute;
}
}

5.采用OGNL表達式創建List/Map集合對象

如果需要一個集合元素的時候(例如List對象或者Map對象),可以使用OGNL中同集合相關的表達式。
使用如下代碼直接生成一個List對象:
<s:set name="list" value="{'zhangming','xiaoi','liming'}" />
<s:iterator value="#list" id="n">
<s:property value="n"/><br>
</s:iterator>

生成一個Map對象:
<s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" />
<s:iterator value="#foobar" >
<s:property value="key"/>=<s:property value="value"/><br>
</s:iterator>

Set標簽用于將某個值放入指定范圍。
scope
:指定變量被放置的范圍,該屬性可以接受applicationsessionrequest pageaction。如果沒有設置該屬性,則默認放置在OGNL Context中。
value
:賦給變量的值.如果沒有設置該屬性,則將ValueStack棧頂的值賦給變量。


6.
采用OGNL表達式判斷對象是否存在于集合中
對于集合類型,OGNL表達式可以使用innot in兩個元素符號。其中,in表達式用來判斷某個元素是否在指定的集合對象中;not in判斷某個元素是否不在指定的集合對象中,如下所示。
in
表達式:
<s:if test="'foo' in {'foo','bar'}">

</s:if>
<s:else>
不在
</s:else>

not in表達式:
<s:if test="'foo' not in {'foo','bar'}">
不在
</s:if>
<s:else>

</s:else>

7.OGNL表達式的投影功能
除了innot in之外,OGNL還允許使用某個規則獲得集合對象的子集,常用的有以下3個相關操作符。
?
:獲得所有符合邏輯的元素。
^
:獲得符合邏輯的第一個元素。
$
:獲得符合邏輯的最后一個元素。
例如代碼:
<s:iterator value="books.{?#this.price > 35}">
<s:property value="title" /> - $<s:property value="price" /><br>
</s:iterator>
在上面代碼中,直接在集合后緊跟.{}運算符表明用于取出該集合的子集,{}內的表達式用于獲取符合條件的元素,this指的是為了從大集合books篩選數據到小集合,需要對大集合books進行迭代,this代表當前迭代的元素。本例的表達式用于獲取集合中價格大于35的書集合。
public class BookAction extends ActionSupport {
private List<Book> books;
....
@Override
public String execute() {
books = new LinkedList<Book>();
books.add(new Book("A735619678", "spring", 67));
books.add(new Book("B435555322", "ejb3.0",15));
}
}

8.property標簽
property
標簽用于輸出指定值:
<s:set name="name" value="'kk'" />
<s:property value="#name"/>
default
:可選屬性,如果需要輸出的屬性值為null,則顯示該屬性指定的值
escape
:可選屬性,指定是否格式化HTML代碼。
value
:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出ValueStack棧頂的值。
id
:可選屬性,指定該元素的標識


9. iterator
標簽
iterator
標簽用于對集合進行迭代,這里的集合包含ListSet和數組。
<s:set name="list" value="{'zhangming','xiaoi','liming'}" />
<s:iterator value="#list" status="st">
<font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>
<s:property /></font><br>
</s:iterator>
value
:可選屬性,指定被迭代的集合,如果沒有設置該屬性,則使用ValueStack棧頂的集合。
id
:可選屬性,指定集合里元素的id
status
:可選屬性,該屬性指定迭代時的IteratorStatus實例。該實例包含如下幾個方法:
int getCount()
,返回當前迭代了幾個元素。
int getIndex()
,返回當前迭代元素的索引。
boolean isEven()
,返回當前被迭代元素的索引是否是偶數
boolean isOdd()
,返回當前被迭代元素的索引是否是奇數
boolean isFirst()
,返回當前被迭代元素是否是第一個元素。
boolean isLast()
,返回當前被迭代元素是否是最后一個元素。

10.if/elseif/else標簽
<s:set name="age" value="21" />
<s:if test="#age==23">
23
</s:if>
<s:elseif test="#age==21">
21
</s:elseif>
<s:else>
都不等
</s:else>

10.url標簽

<s:url action="helloworld_add" namespace="/test"><s:param name="personid" value="23"/></s:url>
生成類似如下路徑:

/struts/test/helloworld_add.action?personid=23
紅色部分為內容路徑。

當標簽的屬性值作為字符串類型處理時, “%”符號的用途是計算OGNL表達式的值。
<s:set name="myurl" value="'http://www.foshanshop.net'"/>
<s:url value="#myurl" /><br>
<s:url value="%{#myurl}" />
輸出結果:
#myurl
http://www.foshanshop.net


11.
表單標簽_checkboxlist復選框

如果集合為list
<s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','.Net'}"/>
生成如下html代碼:

<input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label>
<input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label>
<input type="checkbox" name="list" value="RoR"/><label>RoR</label>
<input type="checkbox" name="list" value="PHP"/><label>PHP</label>

如果集合為MAP
<s:checkboxlist name="map" list="#{1:'
瑜珈用品',2:'戶外用品',3:'球類',4:'自行車
'}" listKey="key" listValue="value" value="{1,2,3}"/>
生成如下html代碼:

<input type="checkbox" name="map" value="1" checked="checked"/><label>
瑜珈用品</label>
<input type="checkbox" name="map" value="2" checked="checked"/><label>
戶外用品
</label>
<input type="checkbox" name="map" value="3" checked="checked"/><label>
球類
</label>
<input type="checkbox" name="map" value="4"/><label>
自行車</label>


12.
表單標簽_checkboxlist復選框
如果集合里存放的是javabean
<%
Person person1 = new Person(1,"
第一個
");
Person person2 = new Person(2,"
第二個
");
List<Person> list = new ArrayList<Person>();
list.add(person1);
list.add(person2);
request.setAttribute("persons",list);
%>
<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
Personid
namePerson的屬性

生成如下html代碼:
<input type="checkbox" name=“beans" value="1"/><label>
第一個</label>
<input type="checkbox" name=“beans" value="2"/><label>
第二個</label>

13.表單標簽_radio單選框
該標簽的使用和checkboxlist復選框相同。
如果集合里存放的是javabean(personidnamePerson的屬性)
< s:radio name="beans" list="#request.persons" listKey="personid" listValue="name"/>
生成如下html代碼:

<input type="radio" name="beans" id="beans1" value="1"/><label>
第一個</label>
<input type="radio" name="beans" id="beans2" value="2"/><label>
第二個
</label>
如果集合為
MAP
<s:radio name="map" list="#{1:'
瑜珈用品',2:'戶外用品',3:'球類',4:'自行車
'}" listKey="key" listValue="value“ value="1"/>
生成如下html代碼:

<input type="radio" name="map" id="map1" value="1"/><label for="map1">
瑜珈用品</label>
<input type="radio" name="map" id="map2" value="2"/><label for="map2">
戶外用品
</label>
<input type="radio" name="map" id="map3" value="3"/><label for="map3">
球類
</label>
<input type="radio" name="map" id="map4" value="4"/><label for="map4">
自行車
</label>
如果集合為
list
<s:radio name="list" list="{'Java','.Net'}" value="'Java'"/>
生成如下html代碼:

<input type="radio" name="list" checked="checked" value="Java"/><label>Java</label>
<input type="radio" name="list" value=".Net"/><label>.Net</label>

14.表單標簽_select下拉選擇框
<s:select name="list" list="{'Java','.Net'}" value="'Java'"/>
<select name="list" id="list">
<option value="Java" selected="selected">Java</option>
<option value=".Net">.Net</option>
</select>
<s:select name="beans" list="#request.persons" listKey="personid" listValue="name"/>
<select name="beans" id="beans">
<option value="1">
第一個</option>
<option value="2">
第二個
</option>
</select>
<s:select name="map" list="#{1:'
瑜珈用品',2:'戶外用品',3:'球類',4:'自行車
'}" listKey="key" listValue="value" value="1"/>
<select name="map" id="map">
<option value="1" selected="selected">
瑜珈用品
</option>
<option value="2">
戶外用品
</option>
<option value="3">
球類
</option>
<option value="4">
自行車
</option>
</select>

 

struts2 OGNL表達式

Struts2 2010-01-29 11:03:00 閱讀301 評論0 字號:大中

OGNLObject Graph Navigation Language的簡稱,詳細相關的信息可以參考:http://www.ognl.org。這里我們只涉及Struts2框架中對OGNL的基本支持。

Struts 2默認的表達式語言是OGNL,原因是它相對其它表達式語言具有下面幾大優勢:

1.  支持對象方法調用,如xxx.doSomeSpecial()

2.  支持類靜態的方法調用和值訪問,表達式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')@tutorial.MyConstant@APP_NAME

3.  支持賦值操作和表達式串聯,如price=100, discount=0.8, calculatePrice(),這個表達式會返回80

4.  訪問OGNL上下文(OGNL context)和ActionContext

5.  操作集合對象。

#%$這三個符號的使用:

“#”主要有三種用途:

1.  訪問OGNL上下文和Action上下文,#相當于ActionContext.getContext();下表有幾個ActionContext中有用的屬性:

名稱

作用

例子

parameters

包含當前HTTP請求參數的Map

#parameters.id[0]作用相當于request.getParameter("id")

request

包含當前HttpServletRequest的屬性(attribute)Map

#request.userName相當于request.getAttribute("userName")

session

包含當前HttpSession的屬性(attribute)的Map

#session.userName相當于session.getAttribute("userName")

application

包含當前應用的ServletContext的屬性(attribute)的Map

#application.userName相當于application.getAttribute("userName")

attr

用于按request > session > application順序訪問其屬性(attribute

#attr.userName相當于按順序在以上三個范圍(scope)內讀取userName屬性,直到找到為止

2.  用于過濾和投影(projecting)集合,如books.{?#this.price<100}

3.  構造Map,如#{'foo1':'bar1', 'foo2':'bar2'}

“%”符號的用途是在標志的屬性為字符串類型時,計算OGNL表達式的值。例如在Ognl.jsp中加入以下代碼:

<hr />
<h3>%的用途</h3>
<p><s:url value="#foobar['foo1']" /></p>
<p><s:url value="%{#foobar['foo1']}" /></p>

OGNL是一個對象,屬性的查詢語言。在OGNL中有一個類型為MapContext(稱為上下文),在這個上下文中有一個根元素(root),對根元素的屬性的訪問可以直接使用屬性名字,但是對于其他非根元素屬性的訪問必須加上特殊符號#

Struts2中上下文為ActionContext,根元素位Value Stack(值堆棧,值堆棧代表了一族對象而不是一個對象,其中Action類的實例也屬于值堆棧的一個)。ActionContext中的內容如下圖:

              |--application

              |

              |--session

context map---|

              |--value stack(root)

              |

              |--request

              |

              |--parameters

              |

              |--attr (searches page, request, session, then application scopes)

             

因為Action實例被放在Value Stack中,而Value Stack又是根元素(root)中的一個,所以對Action中的屬性的訪問可以不使用標記#,而對其他的訪問都必須使用#標記。

引用Action的屬性

<s:property value="postalCode"/>

ActionContext中的其他非根(root)元素的屬性可以按照如下的方式訪問:

<s:property value="#session.mySessionPropKey"/> or

<s:property value="#session["mySessionPropKey"]"/> or

<s:property value="#request["mySessionPropKey"]/>

Action類可以使用ActionContext中的靜態方法來訪問ActionContext

ActionContext.getContext().getSession().put("mySessionPropKey", mySessionObject);

 

OGNLCollectionListsMapsSets

 

生成List的語法為: {e1,e2,e3}.

<s:select label="label" name="name"

list="{'name1','name2','name3'}" value="%{'name2'}" />

上面的代碼生成了一個HTML Select對象,可選的內容為: name1name2name3,默認值為:name2

生成Map的語法為:#{key1:value1,key2:value2}.

<s:select label="label" name="name"

list="#{'foo':'foovalue', 'bar':'barvalue'}" />

上面的代碼生成了一個HTML Select對象,foo名字表示的內容為:foovaluebar名字表示的內容為:barvalue

對于集合,OGNL提供了兩個元素符:innot in,其中in判斷某個元素是否在指定集合中;not in則用于判斷某個元素是否不在指定集合中。

判斷一個對象是否在List內存在:

<s:if test="'foo' in {'foo','bar'}">

   muhahaha

</s:if>

<s:else>

   boo

</s:else>

<s:if test="'foo' not in {'foo','bar'}">

   muhahaha

</s:if>

<s:else>

   boo

</s:else>

取得一個List的一部分:

? – 所有滿足選擇邏輯的對象

^ -   第一個滿足選擇邏輯的對象

$ -   最后一個滿足選擇邏輯的對象

例如:

person.relatives.{? #this.gender == 'male'}

上述代碼取得這個人(person)所有的男性(this.gender==male)的親戚(relatives)

 

Lambda 表達式

 

OGNL支持簡單的Lambda表達式語法,使用這些語法可以建立簡單的lambda函數。

例如:

Fibonacci:

if n==0 return 0;

elseif n==1 return 1;

else return fib(n-2)+fib(n-1);

fib(0) = 0

fib(1) = 1

fib(11) = 89

OGNLLambda表達式如何工作呢?

Lambda表達式必須放在方括號內部,#this表示表達式的參數。例如:

<s:property value="#fib =:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2)+#fib(#this-1)], #fib(11)" />

#fib =:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2)+#fib(#this-1)]定義了一個Lambda表達式,

#fib(11) 調用了這個表達式。

所以上述代碼的輸出為:89

JSP2.1#被用作了JSP EL(表達式語言)的特殊記好,所以對OGNL的使用可能導致問題,

一個簡單的方法是禁用JSP2.1EL特性,這需要修改web.xml文件:

<jsp-config>

    <jsp-property-group>

      <url-pattern>*.jsp</url-pattern>

      <el-ignored>true</el-ignored>

    </jsp-property-group>

</jsp-config>

 

4.6 Tag 語法

代碼示例:

表達式

含義

<p>Username: ${user.username}</p>

一個在標準上下文中的JavaBean對象,可以適用FreemarkerVelocity,JSTL EL等(不是OGNL)。

<s:textfield name="username"/>

Value Stack中的一個username屬性。

<s:url id="es" action="Hello">

<s:param name="request_locale">

    es

</s:param>

</s:url>

<s:a href="%{es}">Espanol</s:a>

引用Value Stack中屬性的另外一種方法。

<s:property

name="#session.user.username" />

Session中的user對象的username屬性。

<s:select

label="FooBar" name="foo"

list="#{'username':'trillian',

    'username':'zaphod'}" />

一個簡單的靜態Map,和put("username","trillian")一樣

 

Struts2OGNL基礎上的增強


  1、值棧(ValueStack)
    Struts2OGNL上下文設置為Struts2中的ActionContext(內部使用的仍然是OgnlContext),并將值棧設為OGNL的根對象。

    我們知道,OGNL上下文中的根對象可以直接訪問,不需要使用任何特殊的標記,而引用上下文中的其他對象則需要使用“#”來標記。由于值棧是 上下文中的根對象,因此可以直接訪問。那么對于值棧中的對象該如何訪問呢?Struts2提供了一個特殊的OGNLPropertyAccessor,它 可以自動查找棧內的所有對象(從棧頂到棧底),直接找到一個具有你所查找的屬性的對象。也就是說,對于值棧中的任何對象都可以直接訪問,而不需要使用 “#”
    假設值棧中有兩個對象:studentemployee,兩個對象都有name屬性,student有學號屬性number,而 employee有薪水屬性salaryemployee先入棧,student后入棧,位于棧頂,那么對于表達式name,訪問的就是student name屬性,因為student對象位于棧頂;表達式salary,訪問的就是employeesalary屬性。正如你所見,訪問值棧中的對象屬 性或方法,無須指明對象,也不用“#”,就好像值棧中的對象都是OGNL上下文中的根對象一樣。這就是Struts2OGNL基礎上做出的改進。
  2、[N]語法
    如上所述,如果想要訪問employeename屬性,應該如何寫表達式呢?我們可以使用[N].xxx(N是從0開始的整數)這樣的語法來指定從哪一個位置開始向下查找對象的屬性,表達式[1].name訪問的就是employee對象的name屬性。
    在使用[N].xxx語法時,要注意位置序號的含義,它并不是表示獲取棧中索引為N的對象,而是截取從位置N開始的部分棧。
  3、top關鍵字
    top用于獲取棧頂的對象,結合[N].xxx語法,我們就可以獲取棧中任意位置的對象。
    如:[0].top,[1].top
  4、訪問靜態成員
    除了使用標準的OGNL表達式訪問靜態字段和靜態方法外,Struts2還允許你不指定完整的類名,而是通過“vs”前綴來調用保存在棧中的靜態字段和靜態方法。
    @vs@FOO_PROPERTY
    
@vs@someMethod()
    
@vs1@someMethod()
    vs表示ValueStack,如果只有vs,那么將使用棧頂對象的類;如果在vs后面跟上一個數字,那么將使用棧中指定位置處的對象類。

  5、值棧中的Action實例
    Struts2框架總是把Action實例放在棧頂。因為Action在值棧中,而值棧又是OGNL中的根,所以引用Action的屬性可以省略“#”標記,這也是為什么我們在結果頁面中可以直接訪問Action的屬性的原因。
  6、Struts2中的命名對象
    Struts2還提供了一些命名對象,這些對象沒有保存在值棧中,而是保存在ActionContext中,因此訪問這些對象需要使用“#”標記。這些命名對象都是Map類型。
   parameters
    用于訪問請求參數。如:#parameters['id']#parameters.id,相當于調用了HttpServletRequest對象的getParameter()方法。

    注意,parameters本質上是一個使用HttpServletRequest對象中的請求參數構造的Map對象,一量對象被創建(在調用Action實例之前就已經創建好了),它和HttpServletRequest對象就沒有了任何關系。
   request
    用于訪問請求屬性。如:#request['user']#request.user,相當于調用了HttpServletRequest對象的getAttribute()方法。

   session
    用于訪問session屬性。如:#session['user']#session.user,相當于調用了HttpSession對象的getAttribute()方法。

   application
    用于訪問application屬性。如:#application['user']#application.user,相當于調用了ServletContextgetAttribute()方法。

   attr
    如果PageContext可用,則訪問PageContext,否則依次搜索requestsessionapplication對象。

 

PS:

JSP 2.1 # 被加入了 JSP EL.中。這可能導致一些應用OGNL#號操作的程序出問題,下面是一種快速修復的方法是在web.xml中加上jsp-config的標簽,這適用于一些支持JSP 2.1 EL表達式的容器,如GlassFish.
<jsp-config>
 <jsp-property-group>
 <url-pattern>*.jsp</url-pattern>
 <el-ignored>true</el-ignored>
 </jsp-property-group>
 </jsp-config>

 本文由用戶 xyang81 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!