Struts2學習之OGNL表達式原理(轉載學習)

一、OGNL表達式基礎知識 

1. 示例:第一個OGNL程序

2. 示例:上下文環境中使用OGNL

3. 示例:使用OGNL調用方法

4. 示例:使用OGNL操作集合  

5. 示例:使用OGNL過濾集合與投影集合

二、OGNL與Struts2

OGNL表達式

OGNL,全稱爲Object-Graph Navigation Language,它是一個功能強大的表達式語言,用來獲取和設置Java對象的屬性,它旨在提供一個更高的更抽象的層次來對Java對象圖進行導航。

OGNL表達式的基本單位是"導航鏈",一般導航鏈由如下幾個部分組成:

  1. 屬性名稱(property) 
  2. 方法調用(method invoke) 
  3. 數組元素

所有的OGNL表達式都基於當前對象的上下文來完成求值運算,鏈的前面部分的結果將作爲後面求值的上下文。例如:names[0].length()。

示例:第一個OGNL程序

  1. public class OGNL1  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         /* 創建一個Person對象 */ 
  6.         Person person = new Person();  
  7.         person.setName("zhangsan");  
  8.           
  9.         try 
  10.         {  
  11.             /* 從person對象中獲取name屬性的值 */ 
  12.             Object value = Ognl.getValue("name", person);  
  13.  
  14.             System.out.println(value);  
  15.         }  
  16.         catch (OgnlException e)  
  17.         {  
  18.             e.printStackTrace();  
  19.         }  
  20.     }  
  21. }  
  22.  
  23. class Person  
  24. {  
  25.     private String name;  
  26.  
  27.     public String getName()  
  28.     {  
  29.         return name;  
  30.     }  
  31.  
  32.     public void setName(String name)  
  33.     {  
  34.         this.name = name;  
  35.     }  

控制檯輸出:

zhangsan

可以看到我們正確的取得了person對象的name屬性值,該getValue聲明如下:

  1. public static <T> T getValue(String expression,Object root)throws OgnlException  
  2.  
  3. Convenience method that combines calls to  parseExpression  and  getValue.   
  4.  
  5. Parameters:  
  6. expression - the OGNL expression to be parsed  
  7. root - the root object for the OGNL expression   
  8. Returns:  
  9. the result of evaluating the expression 

OGNL會根據表達式從根對象(root)中提取值。

示例:上下文環境中使用OGNL

  1. public class OGNL1  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         /* 創建一個上下文Context對象,它是用保存多個對象一個環境 對象 */ 
  6.         Map<String , Object> context = new HashMap<String , Object>();  
  7.  
  8.         Person person1 = new Person();  
  9.         person1.setName("zhangsan");  
  10.           
  11.         Person person2 = new Person();  
  12.         person2.setName("lisi");  
  13.  
  14.         Person person3 = new Person();  
  15.         person3.setName("wangwu");  
  16.  
  17.         /* person4不放入到上下文環境中 */ 
  18.         Person person4 = new Person();  
  19.         person4.setName("zhaoliu");  
  20.  
  21.         /* 將person1、person2、person3添加到環境中(上下文中) */ 
  22.         context.put("person1", person1);  
  23.         context.put("person2", person2);  
  24.         context.put("person3", person3);  
  25.  
  26.         try 
  27.         {  
  28.             /* 獲取根對象的"name"屬性值 */ 
  29.             Object value = Ognl.getValue("name", context, person2);  
  30.             System.out.println("ognl expression \"name\" evaluation is : " + value);  
  31.  
  32.             /* 獲取根對象的"name"屬性值 */ 
  33.             Object value2 = Ognl.getValue("#person2.name", context, person2);  
  34.             System.out.println("ognl expression \"#person2.name\" evaluation is : " + value2);  
  35.  
  36.             /* 獲取person1對象的"name"屬性值 */ 
  37.             Object value3 = Ognl.getValue("#person1.name", context, person2);  
  38.             System.out.println("ognl expression \"#person1.name\" evaluation is : " + value3);  
  39.  
  40.             /* 將person4指定爲root對象,獲取person4對象的"name"屬性,注意person4對象不在上下文中 */ 
  41.             Object value4 = Ognl.getValue("name", context, person4);  
  42.             System.out.println("ognl expression \"name\" evaluation is : " + value4);  
  43.  
  44.             /* 將person4指定爲root對象,獲取person4對象的"name"屬性,注意person4對象不在上下文中 */ 
  45.             Object value5 = Ognl.getValue("#person4.name", context, person4);  
  46.             System.out.println("ognl expression \"person4.name\" evaluation is : " + value5);  
  47.  
  48.             /* 獲取person4對象的"name"屬性,注意person4對象不在上下文中 */ 
  49.             // Object value6 = Ognl.getValue("#person4.name", context, person2);  
  50.             // System.out.println("ognl expression \"#person4.name\" evaluation is : " + value6);  
  51.  
  52.         }  
  53.         catch (OgnlException e)  
  54.         {  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58. }  
  59.  
  60. class Person  
  61. {  
  62.     private String name;  
  63.  
  64.     public String getName()  
  65.     {  
  66.         return name;  
  67.     }  
  68.  
  69.     public void setName(String name)  
  70.     {  
  71.         this.name = name;  
  72.     }  

控制檯輸出:

  1. ognl expression "name" evaluation is : lisi  
  2. ognl expression "#person2.name" evaluation is : lisi  
  3. ognl expression "#person1.name" evaluation is : zhangsan  
  4. ognl expression "name" evaluation is : zhaoliu  
  5. ognl.OgnlException: source is null for getProperty(null"name")  
  6.     at ognl.OgnlRuntime.getProperty(OgnlRuntime.java:2296)  
  7.     at ognl.ASTProperty.getValueBody(ASTProperty.java:114)  
  8.     at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)  
  9.     at ognl.SimpleNode.getValue(SimpleNode.java:258)  
  10.     at ognl.ASTChain.getValueBody(ASTChain.java:141)  
  11.     at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)  
  12.     at ognl.SimpleNode.getValue(SimpleNode.java:258)  
  13.     at ognl.Ognl.getValue(Ognl.java:494)  
  14.     at ognl.Ognl.getValue(Ognl.java:596)  
  15.     at ognl.Ognl.getValue(Ognl.java:566)  
  16.     at com.beliefbetrayal.ognl.OGNL1.main(OGNL1.java:53

對於使用上下文的OGNL,若不指定從哪一個對象中查找"name"屬性,則OGNL直接從根對象(root)查找,若指定查找對象(使用'#'號指定,如#person1),則從指定的對象中查找,若指定對象不在上下文中則會拋出異常,換句話說就是是#person1.name形式指定查找對象則必須要保證指定對象在上下文環境中。

示例:使用OGNL調用方法

  1. public class OGNL2  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         /* OGNL提供的一個上下文類,它實現了Map接口 */ 
  6.         OgnlContext context = new OgnlContext();  
  7.  
  8.         People people1 = new People();  
  9.         people1.setName("zhangsan");  
  10.  
  11.         People people2 = new People();  
  12.         people2.setName("lisi");  
  13.  
  14.         People people3 = new People();  
  15.         people3.setName("wangwu");  
  16.  
  17.         context.put("people1", people1);  
  18.         context.put("people2", people2);  
  19.         context.put("people3", people3);  
  20.           
  21.         context.setRoot(people1);  
  22.  
  23.         try 
  24.         {  
  25.             /* 調用 成員方法 */ 
  26.             Object value = Ognl.getValue("name.length()", context, context.getRoot());  
  27.             System.out.println("people1 name length is :" + value);  
  28.               
  29.             Object upperCase = Ognl.getValue("#people2.name.toUpperCase()", context, context.getRoot());  
  30.             System.out.println("people2 name upperCase is :" + upperCase);  
  31.  
  32.             Object invokeWithArgs = Ognl.getValue("name.charAt(5)", context, context.getRoot());  
  33.             System.out.println("people1 name.charAt(5) is :" + invokeWithArgs);  
  34.  
  35.             /* 調用靜態方法 */ 
  36.             Object min = Ognl.getValue("@java.lang.Math@min(4,10)", context, context.getRoot());  
  37.             System.out.println("min(4,10) is :" + min);  
  38.  
  39.             /* 調用靜態變量 */ 
  40.             Object e = Ognl.getValue("@java.lang.Math@E", context, context.getRoot());  
  41.             System.out.println("E is :" + e);  
  42.         }  
  43.         catch (OgnlException e)  
  44.         {  
  45.             e.printStackTrace();  
  46.         }  
  47.     }  
  48. }  
  49.  
  50. class People  
  51. {  
  52.     private String name;  
  53.  
  54.     public String getName()  
  55.     {  
  56.         return name;  
  57.     }  
  58.  
  59.     public void setName(String name)  
  60.     {  
  61.         this.name = name;  
  62.     }  

控制檯輸出:

  1. people1 name length is :8 
  2. people2 name upperCase is :LISI  
  3. people1 name.charAt(5) is :s  
  4. min(4,10) is :4 
  5. E is :2.718281828459045 

使用OGNL調用方法也十分簡單,對於成員方法調用,只需要給出方法的名稱+(),若有參數,直接寫在括號內,與一般調用Java方法一致。對於靜態方法的調用,需要使用如下格式:@ClassName@method,對於靜態變量需要使用如下格式:@ClassName@field。

示例:使用OGNL操作集合

  1. public class OGNL3  
  2. {  
  3.     public static void main(String[] args) throws Exception  
  4.     {  
  5.         OgnlContext context = new OgnlContext();  
  6.           
  7.         Classroom classroom = new Classroom();  
  8.         classroom.getStudents().add("zhangsan");  
  9.         classroom.getStudents().add("lisi");  
  10.         classroom.getStudents().add("wangwu");  
  11.         classroom.getStudents().add("zhaoliu");  
  12.         classroom.getStudents().add("qianqi");  
  13.           
  14.         Student student = new Student();  
  15.         student.getContactWays().put("homeNumber""110");  
  16.         student.getContactWays().put("companyNumber""119");  
  17.         student.getContactWays().put("mobilePhone""112");  
  18.           
  19.         context.put("classroom", classroom);  
  20.         context.put("student", student);  
  21.         context.setRoot(classroom);  
  22.  
  23.         /* 獲得classroom的students集合 */ 
  24.         Object collection = Ognl.getValue("students", context, context.getRoot());  
  25.         System.out.println("students collection is :" + collection);  
  26.  
  27.         /* 獲得classroom的students集合 */ 
  28.         Object firstStudent = Ognl.getValue("students[0]", context, context.getRoot());  
  29.         System.out.println("first student is : " + firstStudent);  
  30.  
  31.         /* 調用集合的方法 */ 
  32.         Object size = Ognl.getValue("students.size()", context, context.getRoot());  
  33.         System.out.println("students collection size is :" + size);  
  34.  
  35.         System.out.println("--------------------------飄逸的分割線--------------------------");  
  36.           
  37.         Object mapCollection = Ognl.getValue("#student.contactWays", context, context.getRoot());  
  38.         System.out.println("mapCollection is :" + mapCollection);  
  39.  
  40.         Object firstElement = Ognl.getValue("#student.contactWays['homeNumber']", context, context.getRoot());  
  41.         System.out.println("the first element of contactWays is :" + firstElement);  
  42.  
  43.         System.out.println("--------------------------飄逸的分割線--------------------------");  
  44.  
  45.         /* 創建集合 */ 
  46.         Object createCollection = Ognl.getValue("{'aa','bb','cc','dd'}", context, context.getRoot());  
  47.         System.out.println(createCollection);  
  48.  
  49.         /* 創建Map集合 */ 
  50.         Object createMapCollection = Ognl.getValue("#{'key1':'value1','key2':'value2'}", context, context.getRoot());  
  51.         System.out.println(createMapCollection);  
  52.  
  53.     }  
  54. }  
  55.  
  56. class Classroom  
  57. {  
  58.     private List<String> students = new ArrayList<String>();  
  59.  
  60.     public List<String> getStudents()  
  61.     {  
  62.         return students;  
  63.     }  
  64.  
  65.     public void setStudents(List<String> students)  
  66.     {  
  67.         this.students = students;  
  68.     }  
  69. }  
  70.  
  71. class Student  
  72. {  
  73.     private Map<String , Object> contactWays = new HashMap<String , Object>();  
  74.  
  75.     public Map<String , Object> getContactWays()  
  76.     {  
  77.         return contactWays;  
  78.     }  
  79.  
  80.     public void setContactWays(Map<String , Object> contactWays)  
  81.     {  
  82.         this.contactWays = contactWays;  
  83.     }  

控制檯的輸出:

  1. students collection is :[zhangsan, lisi, wangwu, zhaoliu, qianqi]  
  2. first student is : zhangsan  
  3. students collection size is :5 
  4. --------------------------飄逸的分割線--------------------------  
  5. mapCollection is :{homeNumber=110, mobilePhone=112, companyNumber=119}  
  6. the first element of contactWays is :110 
  7. --------------------------飄逸的分割線--------------------------  
  8. [aa, bb, cc, dd]  
  9. {key1=value1, key2=value2} 

OGNL不僅可以操作集合對象,還可以創建集合對象,對集合操作與對屬性的操作沒什麼不同,需要注意的是OGNL認爲List與Array是一樣的。使用OGNL創建List集合時使用{},創建Map對象時使用#{}。

示例:使用OGNL過濾集合與投影集合

  1. public class OGNL4  
  2. {  
  3.     public static void main(String[] args) throws Exception  
  4.     {  
  5.         OgnlContext context = new OgnlContext();  
  6.  
  7.         Humen humen = new Humen();  
  8.         humen.setName("qiuyi");  
  9.         humen.setSex("n");  
  10.         humen.setAge(22);  
  11.         humen.getFriends().add(new Humen("zhangsan" , "n" , 22));  
  12.         humen.getFriends().add(new Humen("lisi" , "f" , 21));  
  13.         humen.getFriends().add(new Humen("wangwu" , "n" , 23));  
  14.         humen.getFriends().add(new Humen("zhaoliu" , "n" , 22));  
  15.         humen.getFriends().add(new Humen("qianqi" , "n" , 22));  
  16.         humen.getFriends().add(new Humen("sunba" , "f" , 20));  
  17.         humen.getFriends().add(new Humen("yangqiu" , "f" , 25));  
  18.           
  19.         context.put("humen", humen);  
  20.         context.setRoot(humen);  
  21.  
  22.         /* OGNL過濾集合的語法爲:collection.{? expression} */ 
  23.         Object filterCollection = Ognl.getValue("friends.{? #this.name.length() > 7}", context, context.getRoot());  
  24.         System.out.println("filterCollection is :" + filterCollection);  
  25.  
  26.         System.out.println("--------------------------飄逸的分割線--------------------------");  
  27.  
  28.         /* OGNL投影集合的語法爲:collection.{expression} */ 
  29.         Object projectionCollection = Ognl.getValue("friends.{name}", context, context.getRoot());  
  30.         System.out.println("projectionCollection is :" + projectionCollection);  
  31.     }  
  32. }  
  33.  
  34. class Humen  
  35. {  
  36.     private String name;  
  37.     private String sex;  
  38.     private int age;  
  39.     private List<Humen> friends = new ArrayList<Humen>();  
  40.  
  41.     public Humen()  
  42.     {  
  43.  
  44.     }  
  45.  
  46.     public Humen(String name , String sex , int age)  
  47.     {  
  48.         this.name = name;  
  49.         this.sex = sex;  
  50.         this.age = age;  
  51.     }  
  52.  
  53.     public String getName()  
  54.     {  
  55.         return name;  
  56.     }  
  57.  
  58.     public void setName(String name)  
  59.     {  
  60.         this.name = name;  
  61.     }  
  62.  
  63.     public String getSex()  
  64.     {  
  65.         return sex;  
  66.     }  
  67.  
  68.     public void setSex(String sex)  
  69.     {  
  70.         this.sex = sex;  
  71.     }  
  72.  
  73.     public int getAge()  
  74.     {  
  75.         return age;  
  76.     }  
  77.  
  78.     public void setAge(int age)  
  79.     {  
  80.         this.age = age;  
  81.     }  
  82.  
  83.     public List<Humen> getFriends()  
  84.     {  
  85.         return friends;  
  86.     }  
  87.  
  88.     public void setFriends(List<Humen> friends)  
  89.     {  
  90.         this.friends = friends;  
  91.     }  
  92.  
  93.     @Override 
  94.     public String toString()  
  95.     {  
  96.         return "Humen [name=" + name + ", sex=" + sex + ", age=" + age + "]";  
  97.     }  

控制檯輸出:

  1. filterCollection is :[Humen [name=zhangsan, sex=n, age=22]]  
  2. --------------------------飄逸的分割線--------------------------  
  3. projectionCollection is :[zhangsan, lisi, wangwu, zhaoliu, qianqi, sunba, yangqiu] 

OGNL可以對集合進行過濾與投影操作,過濾的語法爲collection.{? expression},其中使用"#this"表示集合當前對象(可以與for-each循環比較)。投影的語法爲collection.{expression}。投影和過濾可以看做是數據庫中對錶取列和取行的操作。


Struts2與OGNL

Struts 2支持以下幾種表達式語言:
1. OGNL(Object-Graph Navigation Language),可以方便地操作對象屬性的開源表達式語言; 
2. JSTL(JSP Standard Tag Library),JSP 2.0集成的標準的表達式語言; 
3. Groovy,基於Java平臺的動態語言,它具有時下比較流行的動態語言(如Python、Ruby和Smarttalk等)的一些起特性; 
4. Velocity,嚴格來說不是表達式語言,它是一種基於Java的模板匹配引擎,具說其性能要比JSP好。 


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. 操作集合對象。


——————————————以上內容引用自http://www.blogjava.net/max/archive/2007/04/28/114417.html

平時使用Struts2標籤時會出現一些很奇特的問題,對於OGNL不瞭解的人可能對問題的出現無能爲力或者就算解決了問題也不知道是如何解決的。下面總結一些使用Struts2標籤容易出現的困惑:

問題一:#,%{},$符號

在Struts2標籤屬性中經常會出現"#"或者"%{}"的符號出現,通過上面OGNL表達式基礎的介紹,知道了OGNL上下文中有且僅有一個根對象。Struts2爲我們定義了許多明明對象,他們分別是"ValueStack","Parameters","Session","Request", "Appliction","Attr",其中"ValueStack"被設置爲上下文的根對象。訪問非根對象必須加上"#"號,這就是出現"#"的原因。Struts2中的標的處理類,並不是所有都將標籤的屬性作爲OGNL表達式來看待,有時候我們需要設置動態地值,則必須告訴標籤的處理類該字符串按照OGNL表達式來處理,%{}符號的作用就是告訴標籤的處理類將它包含的字符串按照OGNL表達式處理。 "$"符號用於XML文件中用於獲取動態值,與%{}作用類似。

問題二:%{}符號的影響

Struts2的標籤幾十幾百個,要記住哪一個標籤的處理類將標籤的屬性作爲OGNL表達式是一件很困難的事情,在不清楚處理類的處理方式時怎麼辦,%{}對於標籤處理類來說,若處理類將屬性值作爲普通字符串則%{}符號包含的字符串當做OGNL表達式,若處理類將屬性值作爲OGNL表達式來處理,則直接忽略%{}符號。換句話說,不清楚處理方式的話,可以都使用%{}符號。

問題三:標籤是如何獲得數據

下面是ValueStack的官方描述:

ValueStack allows multiple beans to be pushed in and dynamic EL expressions to be evaluated against it. When evaluating an expression, the stack will be searched down the stack, from the latest objects pushed in to the earliest, looking for a bean with a getter or setter for the given property or a method of the given name (depending on the expression being evaluated).

大致意思:ValueStack允許保存多個bean(也就是Action),並且可以使用表達式語言獲得他們。當評估一個表達式,ValueStack將會從棧頂到棧底的方向被搜索一遍,對於給定的屬性名稱尋找bean的getter或setter方法或尋找給定的方法。

每當一個請求到達Action時,Struts2會將Action對象推入ValueStack中。

  1. <body>   
  2.     username:<s:property value="username"/><br />  
  3.     -------------------詭異的分割線-------------------<br />  
  4.     username:<%= ((HelloWorldAction)ActionContext.getContext().getValueStack().peek()).getUsername() %><br />  
  5.   </body> 

頁面顯示結果:

  1. username:zhangsan  
  2. -------------------詭異的分割線-------------------  
  3. username:zhangsan 

可以看到標籤取值與用Java代碼取值的結果相同,明顯標籤的取值方式更簡練簡潔。OGNL表達式"username"表示了從根對象ValueStack中取出屬性username的值。它會從棧頂到棧底遍歷ValueStack,直到找某一個Action中的"username"屬性。

個人學習心得,難免有錯誤遺漏,歡迎指正討論!

原文鏈接:http://www.cnblogs.com/beliefbetrayal/archive/2012/02/11/2347244.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章