通過實(shí)例深入學(xué)習(xí)Java的Struts框架中的OGNL表達(dá)式使用
Struts 2默認(rèn)的表達(dá)式語言是OGNL,原因是它相對(duì)其它表達(dá)式語言具有下面幾大優(yōu)勢:
1. 支持對(duì)象方法調(diào)用,如xxx.doSomeSpecial();
2. 支持類靜態(tài)的方法調(diào)用和值訪問,表達(dá)式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
3. 支持賦值操作和表達(dá)式串聯(lián),如price=100, discount=0.8, calculatePrice(),這個(gè)表達(dá)式會(huì)返回80;
4. 訪問OGNL上下文(OGNL context)和ActionContext;
5. 操作集合對(duì)象。
下面我們來看OGNL使用的幾個(gè)例子:
示例:上下文環(huán)境中使用OGNL
public class OGNL1 { public static void main(String[] args) { /* 創(chuàng)建一個(gè)上下文Context對(duì)象,它是用保存多個(gè)對(duì)象一個(gè)環(huán)境 對(duì)象 */ Map<String , Object> context = new HashMap<String , Object>(); Person person1 = new Person(); person1.setName("zhangsan"); Person person2 = new Person(); person2.setName("lisi"); Person person3 = new Person(); person3.setName("wangwu"); /* person4不放入到上下文環(huán)境中 */ Person person4 = new Person(); person4.setName("zhaoliu"); /* 將person1、person2、person3添加到環(huán)境中(上下文中) */ context.put("person1", person1); context.put("person2", person2); context.put("person3", person3); try { /* 獲取根對(duì)象的"name"屬性值 */ Object value = Ognl.getValue("name", context, person2); System.out.println("ognl expression \"name\" evaluation is : " + value); /* 獲取根對(duì)象的"name"屬性值 */ Object value2 = Ognl.getValue("#person2.name", context, person2); System.out.println("ognl expression \"#person2.name\" evaluation is : " + value2); /* 獲取person1對(duì)象的"name"屬性值 */ Object value3 = Ognl.getValue("#person1.name", context, person2); System.out.println("ognl expression \"#person1.name\" evaluation is : " + value3); /* 將person4指定為root對(duì)象,獲取person4對(duì)象的"name"屬性,注意person4對(duì)象不在上下文中 */ Object value4 = Ognl.getValue("name", context, person4); System.out.println("ognl expression \"name\" evaluation is : " + value4); /* 將person4指定為root對(duì)象,獲取person4對(duì)象的"name"屬性,注意person4對(duì)象不在上下文中 */ Object value5 = Ognl.getValue("#person4.name", context, person4); System.out.println("ognl expression \"person4.name\" evaluation is : " + value5); /* 獲取person4對(duì)象的"name"屬性,注意person4對(duì)象不在上下文中 */ // Object value6 = Ognl.getValue("#person4.name", context, person2); // System.out.println("ognl expression \"#person4.name\" evaluation is : " + value6); } catch (OgnlException e) { e.printStackTrace(); } } } class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
控制臺(tái)輸出:
ognl expression "name" evaluation is : lisi ognl expression "#person2.name" evaluation is : lisi ognl expression "#person1.name" evaluation is : zhangsan ognl expression "name" evaluation is : zhaoliu ognl.OgnlException: source is null for getProperty(null, "name") at ognl.OgnlRuntime.getProperty(OgnlRuntime.java:2296) at ognl.ASTProperty.getValueBody(ASTProperty.java:114) at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) at ognl.SimpleNode.getValue(SimpleNode.java:258) at ognl.ASTChain.getValueBody(ASTChain.java:141) at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) at ognl.SimpleNode.getValue(SimpleNode.java:258) at ognl.Ognl.getValue(Ognl.java:494) at ognl.Ognl.getValue(Ognl.java:596) at ognl.Ognl.getValue(Ognl.java:566) at com.beliefbetrayal.ognl.OGNL1.main(OGNL1.java:53)
對(duì)于使用上下文的OGNL,若不指定從哪一個(gè)對(duì)象中查找"name"屬性,則OGNL直接從根對(duì)象(root)查找,若指定查找對(duì)象(使用'#'號(hào)指定,如#person1),則從指定的對(duì)象中查找,若指定對(duì)象不在上下文中則會(huì)拋出異常,換句話說就是是#person1.name形式指定查找對(duì)象則必須要保證指定對(duì)象在上下文環(huán)境中。
示例:使用OGNL調(diào)用方法
public class OGNL2 { public static void main(String[] args) { /* OGNL提供的一個(gè)上下文類,它實(shí)現(xiàn)了Map接口 */ OgnlContext context = new OgnlContext(); People people1 = new People(); people1.setName("zhangsan"); People people2 = new People(); people2.setName("lisi"); People people3 = new People(); people3.setName("wangwu"); context.put("people1", people1); context.put("people2", people2); context.put("people3", people3); context.setRoot(people1); try { /* 調(diào)用 成員方法 */ Object value = Ognl.getValue("name.length()", context, context.getRoot()); System.out.println("people1 name length is :" + value); Object upperCase = Ognl.getValue("#people2.name.toUpperCase()", context, context.getRoot()); System.out.println("people2 name upperCase is :" + upperCase); Object invokeWithArgs = Ognl.getValue("name.charAt(5)", context, context.getRoot()); System.out.println("people1 name.charAt(5) is :" + invokeWithArgs); /* 調(diào)用靜態(tài)方法 */ Object min = Ognl.getValue("@java.lang.Math@min(4,10)", context, context.getRoot()); System.out.println("min(4,10) is :" + min); /* 調(diào)用靜態(tài)變量 */ Object e = Ognl.getValue("@java.lang.Math@E", context, context.getRoot()); System.out.println("E is :" + e); } catch (OgnlException e) { e.printStackTrace(); } } } class People { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
控制臺(tái)輸出:
people1 name length is :8 people2 name upperCase is :LISI people1 name.charAt(5) is :s min(4,10) is :4 E is :2.718281828459045
使用OGNL調(diào)用方法也十分簡單,對(duì)于成員方法調(diào)用,只需要給出方法的名稱+(),若有參數(shù),直接寫在括號(hào)內(nèi),與一般調(diào)用Java方法一致。對(duì)于靜態(tài)方法的調(diào)用,需要使用如下格式:@ClassName@method,對(duì)于靜態(tài)變量需要使用如下格式:@ClassName@field。
示例:使用OGNL操作集合
public class OGNL3 { public static void main(String[] args) throws Exception { OgnlContext context = new OgnlContext(); Classroom classroom = new Classroom(); classroom.getStudents().add("zhangsan"); classroom.getStudents().add("lisi"); classroom.getStudents().add("wangwu"); classroom.getStudents().add("zhaoliu"); classroom.getStudents().add("qianqi"); Student student = new Student(); student.getContactWays().put("homeNumber", "110"); student.getContactWays().put("companyNumber", "119"); student.getContactWays().put("mobilePhone", "112"); context.put("classroom", classroom); context.put("student", student); context.setRoot(classroom); /* 獲得classroom的students集合 */ Object collection = Ognl.getValue("students", context, context.getRoot()); System.out.println("students collection is :" + collection); /* 獲得classroom的students集合 */ Object firstStudent = Ognl.getValue("students[0]", context, context.getRoot()); System.out.println("first student is : " + firstStudent); /* 調(diào)用集合的方法 */ Object size = Ognl.getValue("students.size()", context, context.getRoot()); System.out.println("students collection size is :" + size); System.out.println("--------------------------飄逸的分割線--------------------------"); Object mapCollection = Ognl.getValue("#student.contactWays", context, context.getRoot()); System.out.println("mapCollection is :" + mapCollection); Object firstElement = Ognl.getValue("#student.contactWays['homeNumber']", context, context.getRoot()); System.out.println("the first element of contactWays is :" + firstElement); System.out.println("--------------------------飄逸的分割線--------------------------"); /* 創(chuàng)建集合 */ Object createCollection = Ognl.getValue("{'aa','bb','cc','dd'}", context, context.getRoot()); System.out.println(createCollection); /* 創(chuàng)建Map集合 */ Object createMapCollection = Ognl.getValue("#{'key1':'value1','key2':'value2'}", context, context.getRoot()); System.out.println(createMapCollection); } } class Classroom { private List<String> students = new ArrayList<String>(); public List<String> getStudents() { return students; } public void setStudents(List<String> students) { this.students = students; } } class Student { private Map<String , Object> contactWays = new HashMap<String , Object>(); public Map<String , Object> getContactWays() { return contactWays; } public void setContactWays(Map<String , Object> contactWays) { this.contactWays = contactWays; } }
控制臺(tái)的輸出:
students collection is :[zhangsan, lisi, wangwu, zhaoliu, qianqi] first student is : zhangsan students collection size is :5 --------------------------飄逸的分割線-------------------------- mapCollection is :{homeNumber=110, mobilePhone=112, companyNumber=119} the first element of contactWays is :110 --------------------------飄逸的分割線-------------------------- [aa, bb, cc, dd] {key1=value1, key2=value2}
OGNL不僅可以操作集合對(duì)象,還可以創(chuàng)建集合對(duì)象,對(duì)集合操作與對(duì)屬性的操作沒什么不同,需要注意的是OGNL認(rèn)為List與Array是一樣的。使用OGNL創(chuàng)建List集合時(shí)使用{},創(chuàng)建Map對(duì)象時(shí)使用#{}。
示例:使用OGNL過濾集合與投影集合
public class OGNL4 { public static void main(String[] args) throws Exception { OgnlContext context = new OgnlContext(); Humen humen = new Humen(); humen.setName("qiuyi"); humen.setSex("n"); humen.setAge(22); humen.getFriends().add(new Humen("zhangsan" , "n" , 22)); humen.getFriends().add(new Humen("lisi" , "f" , 21)); humen.getFriends().add(new Humen("wangwu" , "n" , 23)); humen.getFriends().add(new Humen("zhaoliu" , "n" , 22)); humen.getFriends().add(new Humen("qianqi" , "n" , 22)); humen.getFriends().add(new Humen("sunba" , "f" , 20)); humen.getFriends().add(new Humen("yangqiu" , "f" , 25)); context.put("humen", humen); context.setRoot(humen); /* OGNL過濾集合的語法為:collection.{? expression} */ Object filterCollection = Ognl.getValue("friends.{? #this.name.length() > 7}", context, context.getRoot()); System.out.println("filterCollection is :" + filterCollection); System.out.println("--------------------------飄逸的分割線--------------------------"); /* OGNL投影集合的語法為:collection.{expression} */ Object projectionCollection = Ognl.getValue("friends.{name}", context, context.getRoot()); System.out.println("projectionCollection is :" + projectionCollection); } } class Humen { private String name; private String sex; private int age; private List<Humen> friends = new ArrayList<Humen>(); public Humen() { } public Humen(String name , String sex , int age) { this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<Humen> getFriends() { return friends; } public void setFriends(List<Humen> friends) { this.friends = friends; } @Override public String toString() { return "Humen [name=" + name + ", sex=" + sex + ", age=" + age + "]"; } }
控制臺(tái)輸出:
filterCollection is :[Humen [name=zhangsan, sex=n, age=22]] --------------------------飄逸的分割線-------------------------- projectionCollection is :[zhangsan, lisi, wangwu, zhaoliu, qianqi, sunba, yangqiu]
OGNL可以對(duì)集合進(jìn)行過濾與投影操作,過濾的語法為collection.{? expression},其中使用"#this"表示集合當(dāng)前對(duì)象(可以與for-each循環(huán)比較)。投影的語法為collection.{expression}。投影和過濾可以看做是數(shù)據(jù)庫中對(duì)表取列和取行的操作。
一些常見問題
平時(shí)使用Struts2標(biāo)簽時(shí)會(huì)出現(xiàn)一些很奇特的問題,對(duì)于OGNL不了解的人可能對(duì)問題的出現(xiàn)無能為力或者就算解決了問題也不知道是如何解決的。下面總結(jié)一些使用Struts2標(biāo)簽容易出現(xiàn)的困惑:
問題一:#,%{},$符號(hào)
在Struts2標(biāo)簽屬性中經(jīng)常會(huì)出現(xiàn)"#"或者"%{}"的符號(hào)出現(xiàn),通過上面OGNL表達(dá)式基礎(chǔ)的介紹,知道了OGNL上下文中有且僅有一個(gè)根對(duì)象。Struts2為我們定義了許多明明對(duì)象,他們分別是"ValueStack","Parameters","Session","Request", "Appliction","Attr",其中"ValueStack"被設(shè)置為上下文的根對(duì)象。訪問非根對(duì)象必須加上"#"號(hào),這就是出現(xiàn)"#"的原因。Struts2中的標(biāo)的處理類,并不是所有都將標(biāo)簽的屬性作為OGNL表達(dá)式來看待,有時(shí)候我們需要設(shè)置動(dòng)態(tài)地值,則必須告訴標(biāo)簽的處理類該字符串按照OGNL表達(dá)式來處理,%{}符號(hào)的作用就是告訴標(biāo)簽的處理類將它包含的字符串按照OGNL表達(dá)式處理。 "$"符號(hào)用于XML文件中用于獲取動(dòng)態(tài)值,與%{}作用類似。
問題二:%{}符號(hào)的影響
Struts2的標(biāo)簽幾十幾百個(gè),要記住哪一個(gè)標(biāo)簽的處理類將標(biāo)簽的屬性作為OGNL表達(dá)式是一件很困難的事情,在不清楚處理類的處理方式時(shí)怎么辦,%{}對(duì)于標(biāo)簽處理類來說,若處理類將屬性值作為普通字符串則%{}符號(hào)包含的字符串當(dāng)做OGNL表達(dá)式,若處理類將屬性值作為OGNL表達(dá)式來處理,則直接忽略%{}符號(hào)。換句話說,不清楚處理方式的話,可以都使用%{}符號(hào)。
問題三:標(biāo)簽是如何獲得數(shù)據(jù)
下面是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允許保存多個(gè)bean(也就是Action),并且可以使用表達(dá)式語言獲得他們。當(dāng)評(píng)估一個(gè)表達(dá)式,ValueStack將會(huì)從棧頂?shù)綏5椎姆较虮凰阉饕槐?,?duì)于給定的屬性名稱尋找bean的getter或setter方法或?qū)ふ医o定的方法。
每當(dāng)一個(gè)請求到達(dá)Action時(shí),Struts2會(huì)將Action對(duì)象推入ValueStack中。
<body> username:<s:property value="username"/><br /> -------------------詭異的分割線-------------------<br /> username:<%= ((HelloWorldAction)ActionContext.getContext().getValueStack().peek()).getUsername() %><br /> </body>
頁面顯示結(jié)果:
username:zhangsan -------------------詭異的分割線------------------- username:zhangsan
可以看到標(biāo)簽取值與用Java代碼取值的結(jié)果相同,明顯標(biāo)簽的取值方式更簡練簡潔。OGNL表達(dá)式"username"表示了從根對(duì)象ValueStack中取出屬性u(píng)sername的值。它會(huì)從棧頂?shù)綏5妆闅vValueStack,直到找某一個(gè)Action中的"username"屬性。
總結(jié)OGNL的使用方法:
1.訪問屬性
名字屬性獲取:
<s:property value="user.username"/><br>
地址屬性獲取:
<s:property value="user.address.addr"/><br>
2.訪問方法
調(diào)用值棧中對(duì)象的普通方法:
<s:property value="user.get()"/><br>
3.訪問靜態(tài)屬性和方法
調(diào)用Action中的靜態(tài)方法:
<s:property value="@struts.action.LoginAction@get()"/>
調(diào)用JDK中的類的靜態(tài)方法:
<s:property value="@java.lang.Math@floor(44.56)"/><br>
調(diào)用JDK中的類的靜態(tài)方法(同上):
<s:property value="@@floor(44.56)"/><br>
調(diào)用JDK中的類的靜態(tài)方法:
<s:property value="@java.util.Calendar@getInstance()"/><br>
調(diào)用普通類中的靜態(tài)屬性:
<s:property value="@struts.vo.Address@TIPS"/><br>
訪問構(gòu)造方法
調(diào)用普通類的構(gòu)造方法:
<s:property value="new struts.vo.Student('李曉紅' , '美女' , 3 , 25).username"/>
4.訪問數(shù)組
獲取List:
<s:property value="testList"/><br>
獲取List中的某一個(gè)元素(可以使用類似于數(shù)組中的下標(biāo)獲取List中的內(nèi)容):
<s:property value="testList[0]"/><br>
獲取Set:
<s:property value="testSet"/><br>
獲取Set中的某一個(gè)元素(Set由于沒有順序,所以不能使用下標(biāo)獲取數(shù)據(jù)):
<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中的某一個(gè)元素(可以使用類似于數(shù)組中的下標(biāo)獲取List中的內(nèi)容):
<s:property value="testMap['m1']"/><br>
獲取List的大小:
<s:property value="testSet.size"/><br>
5.訪問集合 – 投影、選擇(? ^ $)
利用選擇獲取List中成績及格的對(duì)象:<s:property value="stus.{?#this.grade>=60}"/><br>
利用選擇獲取List中成績及格的對(duì)象的username:
<s:property value="stus.{?#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的第一個(gè)對(duì)象的username:
<s:property value="stus.{?#this.grade>=60}.{username}[0]"/><br>
利用選擇獲取List中成績及格的第一個(gè)對(duì)象的username:
<s:property value="stus.{^#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的最后一個(gè)對(duì)象的username:
<s:property value="stus.{$#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的第一個(gè)對(duì)象然后求大小:
<s:property value="stus.{^#this.grade>=600}.{username}.size"/><br>
集合的偽屬性
OGNL能夠引用集合的一些特殊的屬性,這些屬性并不是JavaBeans模式,例如size(),length()等等. 當(dāng)表達(dá)式引用這些屬性時(shí),OGNL會(huì)調(diào)用相應(yīng)的方法,這就是偽屬性.
6.Lambda :[…]
格式::[…]
使用Lambda表達(dá)式計(jì)算階乘:
<s:property value="#f = :[#this==1?1:#this*#f(#this-1)] , #f(4)"/><br>
7.OGNL中#的使用
#可以取出堆棧上下文中的存放的對(duì)象.
獲取Paraments對(duì)象的屬性:<s:property value="#parameters.username"/>
8.OGNL中%的使用
用%{}可以取出存在值堆棧中的Action對(duì)象,直接調(diào)用它的方法.
例如你的Action如果繼承了ActionSupport .那么在頁面標(biāo)簽中,用%{getText('key')}的方式可以拿出國際化信息.
9.OGNL中$的使用
“$”有兩個(gè)主要的用途
- 用于在國際化資源文件中,引用OGNL表達(dá)式
- 在Struts 2配置文件中,引用OGNL表達(dá)式
相關(guān)文章
Java concurrency集合之ConcurrentSkipListSet_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java concurrency集合之ConcurrentSkipListSet的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06JavaFX如何獲取ListView(列表視圖)的選項(xiàng)
這篇文章主要介紹了JavaFX如何獲取ListView(列表視圖)的選項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01java復(fù)制文件和java移動(dòng)文件的示例分享
本文主要介紹了java將文件夾下面的所有的jar文件拷貝到指定的文件夾下面的方法,需要的朋友可以參考下2014-02-02JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比
今天小編就為大家分享一篇關(guān)于JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-02-02Java11中基于嵌套關(guān)系的訪問控制優(yōu)化詳解
Java(和其他語言)通過內(nèi)部類支持嵌套類,要使其正常工作,需要編譯器執(zhí)行一些技巧,下面這篇文章主要給大家介紹了關(guān)于Java11中基于嵌套關(guān)系的訪問控制優(yōu)化的相關(guān)資料,需要的朋友可以參考下2022-01-01Spring Boot應(yīng)用程序同時(shí)支持HTTP和HTTPS協(xié)議的實(shí)現(xiàn)方法
如今,企業(yè)級(jí)應(yīng)用程序的常見場景是同時(shí)支持HTTP和HTTPS兩種協(xié)議,這篇文章考慮如何讓Spring Boot應(yīng)用程序同時(shí)支持HTTP和HTTPS兩種協(xié)議,需要的朋友可以參考下2019-10-10Java的關(guān)鍵字與標(biāo)識(shí)符小結(jié)
這篇文章主要介紹了Java的關(guān)鍵字與標(biāo)識(shí)符,總結(jié)整理了Java各種常見的關(guān)鍵字與標(biāo)識(shí)符功能、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04