(九)Struts2進階之OGNL表達式第一彈

這兩天一直想寫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包。


需要帶的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]]

這個沒啥好講的碉克,記住語法就行了凌唬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棉胀,隨后出現(xiàn)的幾起案子法瑟,更是在濱河造成了極大的恐慌,老刑警劉巖唁奢,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霎挟,死亡現(xiàn)場離奇詭異,居然都是意外死亡麻掸,警方通過查閱死者的電腦和手機酥夭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脊奋,“玉大人熬北,你說我怎么就攤上這事〕舷叮” “怎么了讶隐?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長久又。 經(jīng)常有香客問我巫延,道長,這世上最難降的妖魔是什么地消? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任炉峰,我火速辦了婚禮,結(jié)果婚禮上脉执,老公的妹妹穿的比我還像新娘疼阔。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布婆廊。 她就那樣靜靜地躺著迅细,像睡著了一般。 火紅的嫁衣襯著肌膚如雪否彩。 梳的紋絲不亂的頭發(fā)上疯攒,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機與錄音列荔,去河邊找鬼敬尺。 笑死,一個胖子當(dāng)著我的面吹牛贴浙,可吹牛的內(nèi)容都是我干的砂吞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼崎溃,長吁一口氣:“原來是場噩夢啊……” “哼蜻直!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袁串,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤概而,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后囱修,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赎瑰,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年破镰,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐曼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲜漩,死狀恐怖源譬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孕似,我是刑警寧澤踩娘,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站喉祭,受9級特大地震影響养渴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜臂拓,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望习寸。 院中可真熱鬧胶惰,春花似錦、人聲如沸霞溪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坊饶,卻和暖如春泄伪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匿级。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工蟋滴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痘绎。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓津函,卻偏偏與公主長得像,于是被迫代替她去往敵國和親孤页。 傳聞我的和親對象是個殘疾皇子尔苦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容