這兩天一直想寫OGNL的總結(jié)立宜,但發(fā)現(xiàn)下不了筆邀窃。今天還是咬牙開始寫家坎。
OGNL是Object-Graph Navigation Language的縮寫牍陌,它是一種功能強大的表達式語言疮茄,通過它簡單一致的表達式語法滥朱,可以存取對象的任意屬性,調(diào)用對象的方法力试,遍歷整個對象的結(jié)構(gòu)圖徙邻,實現(xiàn)字段類型轉(zhuǎn)化等功能。它使用相同的表達式去存取對象的屬性畸裳。 ------百度百科
OGNL我們看到最多的就是和Struts2的標(biāo)簽結(jié)合使用缰犁,但其實OGNL離開了Struts2也是可以的,只是用在Struts2中怖糊,就必須和標(biāo)簽庫結(jié)合才能使用帅容。
這篇文章就先講講OGNL不結(jié)合Struts2的一些用法,下篇文章再講OGNL在Struts2中的用法伍伤。
1.使用OGNL前的準(zhǔn)備工作
要使用OGNL并徘,得導(dǎo)入相應(yīng)的jar包。
2.OgnlContext類和Ognl類的介紹
在類中使用OGNL表達式扰魂,和兩個類息息相關(guān)麦乞,分別是OgnlContext類和Ognl類蕴茴。
Ognl類:This class provides static methods for parsing and interpreting OGNL expressions.根據(jù)官方解釋,這個類是提供一些靜態(tài)方法去解析表達式姐直。
OgnlContext:This class defines the execution context for an OGNL expression.該類定義OGNL表達式的執(zhí)行上下文倦淀。
public class OgnlContext extends Object implements Map {}
OgnlContext類實現(xiàn)了Map接口,所以它也是一個Map声畏,可以通過put方法往該上下文環(huán)境中放元素撞叽。該上下文環(huán)境中,有兩種對象:根對象和普通對象砰识。我們可以使用它的setRoot方法設(shè)置根對象能扒。根對象只能有一個,而普通對象則可以有多個辫狼。
3.Ognl獲取普通對象和根對象的方法
在上下文環(huán)境中初斑,有根對象和普通的對象,兩者的獲取方式有所不同:獲取根對象的屬性值膨处,可以直接使用屬性名作為表達式见秤,也可以使用#對象名.屬性名的方式;獲取普通對象的屬性值真椿,則必須使用#對象名.屬性名的方式獲取鹃答。下面舉例說明。
新建一個School類突硝,有學(xué)校名稱和老師的集合兩個屬性
package com.codeliu.ognl;
import java.util.List;
public class School {
private String name;
private List<Teacher> teachers;
public School() {}
public School(String name, List<Teacher> teachers) {
super();
this.name = name;
this.teachers = teachers;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(List<Teacher> teachers) {
this.teachers = teachers;
}
}
新建一個Teacher類测摔,有姓名、年齡解恰、性別三個屬性
package com.codeliu.ognl;
public class Teacher {
private String name;
private String gender;
private int age;
public Teacher(String name, String gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
public Teacher() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
新建一個測試類Test
public class Test {
public static void main(String[] args) throws OgnlException {
// 定義一個老師對象
Teacher t1 = new Teacher("CodeTiger", "男", 22);
List<Teacher> lists = new ArrayList<Teacher>();
lists.add(t1);
// 定義一個學(xué)校
School s = new School("NJUPT", lists);
// 創(chuàng)建一個OgnlContext實例對象
OgnlContext context = new OgnlContext();
// 把學(xué)校和老師放入上下文環(huán)境中
context.put("t1", t1);
context.put("s", s);
// 設(shè)置學(xué)校為根對象
context.setRoot(s);
// 會覆蓋前面的根對象
// context.setRoot(t1);
// 解析表達式
// Object expression = Ognl.parseExpression("#s.name");
Object expression = Ognl.parseExpression("name");
// Object expression = Ognl.parseExpression("s.name"); // 出錯
// 直接獲取根對象的信息,使用#獲取上下文中的屬性值時锋八,必須使用帶Map context參數(shù)getValue方法,指定上下文環(huán)境
// 如果是獲取根對象护盈,沒有使用#挟纱,則可以使用不帶Map context參數(shù)getValue方法,我們也可以使用#獲取根對象屬性值
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 獲取普通對象的屬性
expression = Ognl.parseExpression("#t1.gender");
// 此時必須指定上下文環(huán)境腐宋,不然找不到
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過根對象獲取普通對象的屬性
// expression = Ognl.parseExpression("teachers[0].name");
expression = Ognl.parseExpression("#s.teachers[0].name");
// expression = Ognl.parseExpression("s.teachers[0].name"); // Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
運行輸出
NJUPT
男
CodeTiger
以上有幾個地方要注意:
1.根對象只能有一個紊服,當(dāng)你嘗試設(shè)置多個的時候,后面的會覆蓋前面的胸竞。
2.訪問根對象中的屬性欺嗤,可以直接使用屬性名,或者#對象名.屬性名卫枝,但不能使用對象名.屬性名剂府,不然它會認(rèn)為這是根對象中的一個屬性叫對象名,然后訪問這個屬性的名叫屬性名的屬性值剃盾。(說的我都懵了)
比如我上面獲取學(xué)習(xí)的名稱腺占,使用下面的表達式
Object expression = Ognl.parseExpression("s.name"); // 出錯
則會出現(xiàn)下面的錯誤
Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s
提示你找不到School類中的s屬性淤袜,憑你debug多年的經(jīng)驗,應(yīng)該一眼就能看出問題所在衰伯。
3.不使用#獲取根對象中的屬性值時铡羡, 使用getValue方法可以不指定上下文環(huán)境,因為第三個參數(shù)已經(jīng)得到了根對象意鲸,它指定去根對象中找烦周。如果獲取普通對象的屬性,則必須在getValue方法中指定上下文環(huán)境怎顾,因為你不知道它找不到读慎。
4.通過根對象間接獲取普通對象的屬性時,比如List集合槐雾,使用下標(biāo)去獲取夭委。下面會詳細(xì)講。
4.Ognl訪問非靜態(tài)方法募强、靜態(tài)方法株灸、靜態(tài)字段的方法
Ognl不但可以訪問我們自己定義的類的屬性和方法,還可以訪問Java API中的靜態(tài)方法和靜態(tài)字段擎值』派眨看下面的代碼
// 直接調(diào)用根對象中School的getName方 法獲取學(xué)校名
// expression = Ognl.parseExpression("getName()");
expression = Ognl.parseExpression("#s.getName()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 調(diào)用普通對象中的非靜態(tài)方法
// expression = Ognl.parseExpression("#t1.getName()");
expression = Ognl.parseExpression("#t1.getName().length()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 調(diào)用根對象中的靜態(tài)方法
expression = Ognl.parseExpression("getTeacherName()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 調(diào)用API中的靜態(tài)方法
expression = Ognl.parseExpression("@java.lang.Math@floor(4.5)");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 調(diào)用API中的靜態(tài)屬性
expression = Ognl.parseExpression("@java.lang.Math@PI");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
我特地在School類中增加了一個靜態(tài)方法獲取老師集合中第一個老師的姓名,然后使用Ognl調(diào)用它鸠儿。
輸出結(jié)果如下
NJUPT
9
CodeTiger
4.0
3.141592653589793
5.Ognl獲取數(shù)組屹蚊、集合、Map中元素的方法
Ognl表達式可以創(chuàng)建實例對象进每,以及獲取實例對象中的屬性淑翼,下面我們看看怎么通過Ognl創(chuàng)建一個集合、map以及通過Ognl獲取里面的元素品追。
public class Test2 {
public static void main(String[] args) throws OgnlException {
OgnlContext context = new OgnlContext();
// 通過Ognl可以創(chuàng)建java的實例對象,只有是類的完整路徑
Object expression = Ognl.parseExpression("new java.util.ArrayList()");
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過Ognl可以創(chuàng)建一個初始化的List
expression = Ognl.parseExpression("{'a', 'b', 'c', 'd'}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過Ognl可以創(chuàng)建一個初始化的Map冯丙,注意此時得加上#符號
expression = Ognl.parseExpression("#{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
// 創(chuàng)建指定類型的Map
// expression = Ognl.parseExpression("#@java.util.TreeMap@{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過Ognl訪問數(shù)組中的元素
String[] name1 = {"liu", "xu"};
context.put("name1", name1);
// 直接通過數(shù)組名+下標(biāo)
expression = Ognl.parseExpression("#name1[1]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過Ognl訪問集合中的元素
List<String> name2 = new ArrayList<String>();
Collections.addAll(name2, name1);
context.put("name2", name2);
// 直接通過集合名+下標(biāo)
expression = Ognl.parseExpression("#name2[0]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通過Ognl訪問Map中的元素
Map<Integer, String> name3 = new HashMap<Integer, String>();
name3.put(1, "liu");
name3.put(2, "xu");
context.put("name3", name3);
// 直接通過map名+key
expression = Ognl.parseExpression("#name3[1]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
輸出
[]
[a, b, c, d]
{a=aa, b=bb, c=cc, d=dd}
xu
liu
liu
注意點
1.創(chuàng)建集合不用加#肉瓦,創(chuàng)建map要加#。
2.創(chuàng)建類的一個對象胃惜,要使用類的完整路徑泞莉。
3.要創(chuàng)建帶有初始化值的指定類型的List或Map,可以這樣#@java.util.TreeMap@{'key':'value','key':'value',......}船殉。
6.Ognl中的投影和過濾
無論投影還是過濾鲫趁,都是針對于數(shù)組、集合和Map而言利虫。
投影:把集合中所有對象的某個屬性抽出來挨厚,單獨構(gòu)成一個新的集合對象堡僻。語法如下
collection.{expression}
過濾:將滿足條件的對象,構(gòu)成一個新的集合返回疫剃。語法如下
collection.{?|^|$ expression}
上面?^$的含義如下
?:獲得所有符合邏輯的元素钉疫。
^:獲得符合邏輯的第一個元素。
$:獲得符合邏輯的最后一個元素巢价。
了解了上面的牲阁,我們就看看下面的代碼
public class Test3 {
public static void main(String[] args) throws OgnlException {
Teacher t1 = new Teacher("liu", "男", 22);
Teacher t2 = new Teacher("xu", "女", 22);
Teacher t3 = new Teacher("qian", "男", 30);
Teacher t4 = new Teacher("li", "女", 35);
List<Teacher> lists = new ArrayList<Teacher>();
Collections.addAll(lists, new Teacher[] {t1, t2, t3, t4});
OgnlContext context = new OgnlContext();
context.put("teachers", lists);
// 把所有老師的名字拿出來,投影
Object expression = Ognl.parseExpression("#teachers.{name}");
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年齡在20-30之間的所有老師拿出來壤躲,過濾
expression = Ognl.parseExpression("#teachers.{? #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年齡在20-30之間的第一個老師拿出來城菊,過濾
expression = Ognl.parseExpression("#teachers.{^ #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年齡在20-30之間的最后一個老師拿出來,過濾
expression = Ognl.parseExpression("#teachers.{$ #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
輸出
[liu, xu, qian, li]
[Teacher [name=liu, gender=男, age=22], Teacher [name=xu, gender=女, age=22]]
[Teacher [name=liu, gender=男, age=22]]
[Teacher [name=xu, gender=女, age=22]]
這個沒啥好講的碉克,記住語法就行了凌唬。