2009年7月8日 星期三

[Struts2] 探討 OGNL 運算功能

關於 OGNL (Object-Graph Navigation Language),我們之前已經提過相當的多了!不過那些都只是在 navigate 的部份,也就是如何將資料在 String-based HTTP 與 Java-type 之間做轉換與存取。熟悉 JSP 的 Expression Language (EL) 的 programmers 來說,EL 不僅僅提供了存取資料的功能,也提供好用的運算功能!身為比 EL 更強大的 OGNL 來說,這也是基本的配備!

OGNL and Collection

之前我們有提過 OGNL 如何在 Collection 型態的物件進行 navigate,但不只這樣喔~對於 Collection 型態的物件,OGNL 提供了一些好用的功能:1) 取得資訊、2) 快速建立 與 3) Filter and Project。

1. 取得資訊
如果是要取得 Collection 中的資料,我在內建 OGNL Type Converter 中就有提過,不過我在這裡想要說得是,如果我們想要知道 Collection 中的大小,那我們是不是要將 Collection 的大小先儲存到某個 property 中?答案當然是不用!OGNL 怎麼不會提供這樣的功能勒~不僅僅是 Collection 大小,就連判斷是否為 empty 都有支援。
如果我們要取得 Array 的大小,我們就要使用 array.length;如果是 list,則是 list.size;而 map 的話就與 list 是相同的。而 empty 的判斷只有 list 和 map 有提供,使用都是 list.isEmpty。

2. 快速建立
在 JSP 頁面中所使用的 Collection 型態物件,不見得都要屬於 Action 的 property,OGNL 提供了快速建立 Collection 物件的機制,讓 programmers 可以容易的建立 list 或 map,這對於在頁面上提供一個 select 下拉式選單來說是很方便的。不過目前我不想提到關於 select 選單的部份,主要先考慮如何快速建立 list 或 map,對於下列的 Java code 你應該不陌生:
public List<String> createList() {
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("Silver");
list.add("Welcome");
return list;
}
這段程式碼主要就是建立一個 list 並且容納我們想要的字串物件,對於 OGNL 來說,要建立一個一樣的 list,簡直是易如反掌:

{"Hello", "Silver", "Welcome"}

別懷疑自己的眼睛,沒錯!就是這麼簡單~我們只要短短的 OGNL 就可以幫我們實現出一串的 Java code。現在的你是否也想要知道 map 的建立呢?別急,我們先看看一段程式碼:
public Map<Integer, String> createMap() {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1,"Hello");
map.put(3,"Silver");
map.put(100,"Welcome");
return map;
}
這同樣也是你很熟悉的程式碼,我想我就直接告訴你這樣的 OGNL 會是:
#{1: "Hello", 3: "Silver", 100:"Welcome"}

看到這裡,連我自己都不禁懷疑,到底 OGNL 在哪裡需要 # 符號,在哪裡又不需要呢?目前我還看不透,不過自己在使用時就記住,list 不用 # 而 map 需要~

3. Filter and Project
OGNL 除了上面兩種功能外,另外還提供了過濾 (filter) 的功能與投影 (Project),這看起來匪夷所思,實際上卻是很好用的東西。Filter 就是 OGNL 提供讓 programmers 針對 Collection 撰寫 rule 來取出我們想要的東西;Project 就是將某個 Collection 指取出部份的資料並投影到另一個 Collection 之中。這兩個東西乍看之下好像很類似,其實不然,直接舉個例子應該會比較清楚。假設我們有一個 User 物件:
public class User {
private String name;
private int age;
//getter and setter methods
}
然後,我們有一個 List 儲存一堆的 user 為 users,這次我們先展示 filter 功能,如果我們將 OGNL 撰寫成:
users.{? #this.age > 18}

則 OGNL 會幫我們將 users 中的所有 User 物件的 age 進行過濾,並且依照我們所指是的 rule 完成,而 rule 中的 #this 代表每一次 OGNL 迭代所指向的目標物件 (類似於 iterator.next())。
接下來是 project 的部份,同樣的我們使用 users 來示範,如果我們的 OGNL 為:
users.{name+' '+age}

則 OGNL 會幫我們將 users 中的所有 User 物件的 name 與 age 取出,並用一個空白字串組成新的字串,在存成一個新的 list 之中。當然,我們可以結合 filter 與 project:

users.{? #this.age > 18}.{name+' '+age}

這個結果就是上面兩個 OGNL 的結合。不知道你是否有注意到一個規則,當我們在使用 filter 時,我們的撰寫方式為:

collectionName.{? expression }

反之,當我們使用 project 時,我們的 OGNL 撰寫為:

collectionName.{ expression }

也就是 filter 的 expression 之前有加 ? 符號,而 project 沒有。

Other Functions

OGNL 不僅提供上述的功能,另外提供對於運算方面的功能以及一些特殊的功能:1) 運算功能、2) 呼叫 method 與 3) 存取 static method 與 field。

1) 運算功能
OGNL 提供了多種型態的變數讓 programmers 可以快速的宣告:
  • char - 'a'
  • String - "a" 或 'silver'
  • boolean - True, False
  • int - 123
  • BigDecimal - 123b
  • BigInteger - 123h
另外就是關於運算的部份,就如同 Expression Language(EL) 一樣,OGNL 提供了許多的運算:
  • add: 1+2, "hello"+"silver"
  • subtract: 2-1
  • multiply: 2*1
  • divide: 6/3
  • modules: 5%2
  • increment: a++, ++a
  • decrement: a--, --a
  • equality: a==b
  • less than: a<b
  • greater than: a>b
2) 呼叫 method
對於熟悉 EL 的 programmer 來說,最希望的一件事就是 EL 能夠呼叫 method,不過很可惜的,目前為止,EL 還無法做到~不過別氣餒,OGNL 身為 EL 的強化版,對於 programmer 多年的渴望終於有了回應,OGNL 提供 programmers 可以呼叫 method 的功能,而且使用上相當的簡單。假設我們的 User class 有一個 method 為 toString(),如果我們在 OGNL 想要呼要的話,只要簡單的撰寫成:

user.toString()

沒錯!就是這麼簡單,就如同我們在 Java 中撰寫呼叫 method 一樣~

3) 存取 static method 與 field
OGNL 除了針對 instance 的 field 與 method 做呼叫外,對於 static 的 field 與 method 也是可以呼叫的:

@fullClassName@[field 或 method]

不過要注意的是,我們在 struts.xml 檔案中需要在設定一個 constant:
<constant name="struts.ognl.allowStaticMethodAccess"
value="true" />

這樣我們才可以開始使用 OGNL 呼叫 static method。假設我們在 User class 中有一個 static int age,我們在 OGNL 中就可以這樣呼叫:

@silver8250.bean.User@age

要注意的部份只有 static 呼叫是要用 @ 符號來呼叫,這點記住就好了!其他的呼叫都是一樣的,當然我們可以取得某個 static 物件後,再採用原來的呼叫方式取得更進一步的 field 等:

@silver8250.bean.User@nextUser.name

NOTE:
請盡量保持 JSP 頁面的簡單,雖然 OGNL 擁有運算的能力,不過我還是建議你將這些運算移到 action 中執行,畢竟 JSP 頁面屬於 View,他只負責將 data 顯示給使用者,我們不應該將運算的動作在這裡執行,這樣會破壞 MVC 原先美好的本意!除非有必要這樣做,否則請饒了日後 maintain 的人吧~

沒有留言: