Spring Expression Language(SpEL) 4 學(xué)習(xí)筆記

前言

時(shí)隔多年重新開始學(xué)習(xí)Spring,之前只用過(guò)Spring 2失暴,掰掰指頭已經(jīng)過(guò)了快10年了。
看到書里提到了SpEL微饥,一頭霧水逗扒,在2的時(shí)代好像從來(lái)沒(méi)見過(guò)。把學(xué)習(xí)到的做個(gè)總結(jié)畜号。

SpEL概述

SpEL是Spring內(nèi)置的表達(dá)式語(yǔ)言缴阎,語(yǔ)法與OGNL等其他表達(dá)式語(yǔ)言十分類似。SpEL設(shè)計(jì)之初就是朝著做一個(gè)表達(dá)式語(yǔ)言的通用框架简软,可以獨(dú)立運(yùn)行蛮拔。

SpEL的主要相關(guān)類

SpEL對(duì)表達(dá)式語(yǔ)法解析過(guò)程進(jìn)行了很高的抽象,抽象出解析器痹升、表達(dá)式建炫、解析上下文、估值(Evaluate)上下文等對(duì)象疼蛾,非常優(yōu)雅的表達(dá)了解析邏輯肛跌。主要的對(duì)象如下:

類名 說(shuō)明
ExpressionParser 表達(dá)式解析器接口,包含了(Expression) parseExpression(String), (Expression) parseExpression(String, ParserContext)兩個(gè)接口方法
ParserContext 解析器上下文接口察郁,主要是對(duì)解析器Token的抽象類衍慎,包含3個(gè)方法:getExpressionPrefix,getExpressionSuffixisTemplate,就是表示表達(dá)式從什么符號(hào)開始什么符號(hào)結(jié)束皮钠,是否是作為模板(包含字面量和表達(dá)式)解析稳捆。一般保持默認(rèn)。
Expression 表達(dá)式的抽象麦轰,是經(jīng)過(guò)解析后的字符串表達(dá)式的形式表示乔夯。通過(guò)expressionInstance.getValue方法,可以獲取表示式的值款侵。也可以通過(guò)調(diào)用getValue(EvaluationContext)末荐,從評(píng)估(evaluation)上下文中獲取表達(dá)式對(duì)于當(dāng)前上下文的值
EvaluationContext 估值上下文接口,只有一個(gè)setter方法:setVariable(String, Object)新锈,通過(guò)調(diào)用該方法甲脏,可以為evaluation提供上下文變量

完整的例子:

public static void main(String[] args) {        
    String greetingExp = "Hello, #{ #user }";                             (1)     
    ExpressionParser parser = new SpelExpressionParser();           (2)
    EvaluationContext context = new StandardEvaluationContext();        
    context.setVariable("user", "Gangyou");        (3)

    Expression expression = parser.parseExpression(greetingExp, 
        new TemplateParserContext());     (4)
    System.out.println(expression.getValue(context, String.class)); (5)
}

代碼解釋:

  1. 創(chuàng)建一個(gè)模板表達(dá)式,所謂模板就是帶字面量和表達(dá)式的字符串。其中#{}表示表達(dá)式的起止剃幌,#user是表達(dá)式字符串聋涨,表示引用一個(gè)變量。
  2. 創(chuàng)建表達(dá)式解析器负乡,SpEL框架創(chuàng)建了一個(gè)語(yǔ)言無(wú)關(guān)的處理框架牍白,所以對(duì)于其他的表達(dá)式語(yǔ)言,完全可以創(chuàng)建不同的ExpressionParser抖棘。在這里我們學(xué)習(xí)的是SpEL所以使用SpelExpressionParser()
  3. 通過(guò)evaluationContext.setVariable可以在上下文中設(shè)定變量茂腥。
  4. 解析表達(dá)式,如果表達(dá)式是一個(gè)模板表達(dá)式切省,需要為解析傳入模板解析器上下文最岗。如果不傳入模板解析器上下文,指定表達(dá)式為模板朝捆,那么表達(dá)式字符串Hello, #{ #user }般渡,解析器會(huì)首先去嘗試解析Hello。例子中的模板表達(dá)式芙盘,與'Hello, ' + #user是等價(jià)的驯用。
  5. 使用Expression.getValue()獲取表達(dá)式的值,這里傳入了Evalution上下文儒老,第二個(gè)參數(shù)是類型參數(shù)蝴乔,表示返回值的類型。

SpEL語(yǔ)法

本節(jié)介紹下SpEL中的各類語(yǔ)法驮樊,在語(yǔ)法中會(huì)發(fā)現(xiàn)很多熟悉的影子薇正,比如Java,JavaScript, Groovy等等囚衔。依次介紹SpEL中的

  • 字面量
  • 數(shù)組和Map字面量
  • 二元操作符和三元操作符
  • 來(lái)自Groovy的操作符
  • 數(shù)組列表和Map的訪問(wèn)
  • Java Bean屬性訪問(wèn)
  • Java Bean方法調(diào)用
  • Java 類型訪問(wèn)
  • Java 實(shí)例訪問(wèn)

字面量

SpEL支持如下類型的字面量:

類型 說(shuō)明 示例
數(shù)字 支持任何數(shù)字類型挖腰,包括了各種進(jìn)制的數(shù)字,科學(xué)計(jì)數(shù)法等 6.0221415E+23练湿,0xFFFFFFFF
布爾量 true, false
字符串 用單引號(hào)包圍的字符串猴仑,單引號(hào)要用2個(gè)單引號(hào)轉(zhuǎn)義 'Gangyou''s, Blog'
日期 沒(méi)寫成功 //TODO
null 直接轉(zhuǎn)成字符串null null

屬性

SpEL對(duì)屬性的訪問(wèn)遵循Java Bean語(yǔ)法,對(duì)于表達(dá)式中的屬性都要提供相應(yīng)的setter鞠鲜。

比如這樣一個(gè)Bean:

private static class Person {    
    private String firstName;    
    private String lastName;    
    public Person(String firstName, String lastName) {        
         this.firstName = firstName;        
         this.lastName = lastName;    
    }    
    public String getFirstName() {        
        return firstName;    
    }    
    public String getLastName() {        
        return lastName;    
    }
}

表達(dá)式firstName + ' ' + lastName就等價(jià)于person.getFirstName() + " " + person.getLastName()。如果屬性本身是對(duì)象断国,還支持嵌套屬性贤姆,如person.address.city,就等價(jià)于person.getAddress().getCity()

數(shù)組稳衬、列表和Map

數(shù)組和列表都可以用過(guò)數(shù)字下標(biāo)進(jìn)行訪問(wèn)霞捡,比如list[0]

Map的訪問(wèn)就類似JavaScript的訪問(wèn)方式薄疚,使用key訪問(wèn)碧信。例如java代碼map.put("name", "gangyou")其中的map對(duì)象赊琳,可以通過(guò)map['name']獲取到字符串gangyou

內(nèi)聯(lián)的數(shù)組和Map

Spel允許在表達(dá)式內(nèi)創(chuàng)建數(shù)組(列表)和Map砰碴,如{1,2,3,4},{'firstName': 'Gang', 'lastName': 'You'}躏筏。

方法調(diào)用

SpEL使用java的相同語(yǔ)法進(jìn)行方法調(diào)用,如'Hello.concat(', World!')`呈枉,輸出** Hello, World!**趁尼。

類型

SpEL中可以使用特定的Java類型,經(jīng)常用來(lái)訪問(wèn)Java類型中的靜態(tài)屬性或靜態(tài)方法猖辫,需要用T()操作符進(jìn)行聲明酥泞。括號(hào)中需要包含類名的全限定名,也就是包名加上類名啃憎。唯一例外的是芝囤,SpEL內(nèi)置了java.lang包下的類聲明,也就是說(shuō)java.lang.String可以通過(guò)T(String)訪問(wèn)辛萍,而不需要使用全限定名悯姊。

兩個(gè)例子: T(Math).random()T(java.lang.Math).random()

創(chuàng)建實(shí)例

使用new可以直接在SpEL中創(chuàng)建實(shí)例,需要?jiǎng)?chuàng)建實(shí)例的類要通過(guò)全限定名進(jìn)行訪問(wèn)叹阔,如:new java.util.Date().now()

二元操作符

SpEL中的二元操作符同Java中的二元操作符挠轴,包括了數(shù)學(xué)運(yùn)算符、位運(yùn)算符耳幢、關(guān)系運(yùn)算符等等岸晦。

三元操作符

SpEL的三元操作符主要是 ** if else then ** 操作符 condition ? true statement : false statement與Java中的一致。

另外一個(gè)就是借鑒自Groovy的 Elvis 操作符?:睛藻,Elvis就是貓王启上!

22571c16e399948037ed26756945c6fd.jpg

這個(gè)操作符的樣子就跟貓王的發(fā)型一樣:)。

舉一個(gè)例子

String expressionStr1 = " name != null ? name : 'Default Value'";
String expressionStr2 = "name ?: 'Default Value'";

上面的兩個(gè)表達(dá)式都是先判斷 getName()的返回值是不是null店印,如果是就返回Default Value冈在,通過(guò)下面的Elvis操作符可以讓代碼更加的清晰。

安全訪問(wèn)符

同樣借鑒自Groovy按摘,在SpEL中引入了安全訪問(wèn)符Safe Navigator Operator——.?包券,解決了很大問(wèn)題。相信每個(gè)Javaer都遇到過(guò)NullPointException的運(yùn)行時(shí)異常炫贤,通常是對(duì)象還未實(shí)例化或者找不到對(duì)象溅固,卻訪問(wèn)對(duì)象屬性造成的。通過(guò)安全訪問(wèn)符就可以避免這樣的問(wèn)題兰珍。

String expStr = "thisMayBeNull.?property"

這句表達(dá)式在求值的時(shí)候侍郭,不會(huì)因?yàn)?code>thisMayBeNull是Null值而拋出NullPointException,而是會(huì)簡(jiǎn)單的返回null。個(gè)人認(rèn)為此處結(jié)合Elvis操作符亮元,是一個(gè)很完善的處理方式猛计。

集合選擇

同樣借鑒自Groovy,SpEL提供了集合選擇語(yǔ)法爆捞,如下面的例子:

List<Inventor> list = (List<Inventor>) parser.parseExpression(
        "Members.?[Nationality == 'Serbian']").getValue(societyContext);

如果Members List不是Null奉瘤,則在列表中選擇getNationality() == 'Serbian'的對(duì)象集合返回。這個(gè)很類似于Java 8中的stream and filter方式嵌削,只是更加的簡(jiǎn)潔毛好。

[]中間可以利用任何的布爾表達(dá)式,創(chuàng)建篩選條件苛秕。例如age > 18肌访,name.startsWith('You')等等。

自定義函數(shù)

SpEL提供了Java的基礎(chǔ)功能艇劫,也引入了3個(gè)借鑒自Groovy的特性語(yǔ)法提供更為簡(jiǎn)潔的表達(dá)能力吼驶。作為一款設(shè)計(jì)為框架的語(yǔ)言,更提供了自定義的擴(kuò)展能力店煞。

比如下面的例子:


public abstract class StringUtils {

    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder();
        for (int i = 0; i < input.length(); i++)
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }

    public static void main(String[] args){
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();

        context.registerFunction("reverseString",
        StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));

        String helloWorldReversed = parser.parseExpression(
            "#reverseString('hello')").getValue(context, String.class);
    }
}

通過(guò)使用StandardEvalutinContext.registerFunction可以注冊(cè)自定義的函數(shù)蟹演,唯一的一點(diǎn)要求就是需要在表達(dá)式中通過(guò)#注冊(cè)函數(shù)名的方式引用函數(shù)。

參考資料

Spring Expression Language 官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顷蟀,一起剝皮案震驚了整個(gè)濱河市酒请,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸣个,老刑警劉巖羞反,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異囤萤,居然都是意外死亡昼窗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門涛舍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)澄惊,“玉大人,你說(shuō)我怎么就攤上這事富雅〉” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵没佑,是天一觀的道長(zhǎng)毕贼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)图筹,這世上最難降的妖魔是什么帅刀? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮远剩,結(jié)果婚禮上扣溺,老公的妹妹穿的比我還像新娘。我一直安慰自己瓜晤,他們只是感情好锥余,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痢掠,像睡著了一般驱犹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上足画,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天雄驹,我揣著相機(jī)與錄音,去河邊找鬼淹辞。 笑死医舆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的象缀。 我是一名探鬼主播蔬将,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼央星!你這毒婦竟也來(lái)了霞怀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莉给,失蹤者是張志新(化名)和其女友劉穎毙石,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禁谦,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胁黑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了州泊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丧蘸。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遥皂,靈堂內(nèi)的尸體忽然破棺而出力喷,到底是詐尸還是另有隱情,我是刑警寧澤演训,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布弟孟,位于F島的核電站,受9級(jí)特大地震影響样悟,放射性物質(zhì)發(fā)生泄漏拂募。R本人自食惡果不足惜庭猩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陈症。 院中可真熱鬧蔼水,春花似錦、人聲如沸录肯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)论咏。三九已至优炬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間厅贪,已是汗流浹背蠢护。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留养涮,地道東北人糊余。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像单寂,于是被迫代替她去往敵國(guó)和親贬芥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理宣决,服務(wù)發(fā)現(xiàn)蘸劈,斷路器,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評(píng)論 6 342
  • 前言 人生苦多尊沸,快來(lái) Kotlin 威沫,快速學(xué)習(xí)Kotlin! 什么是Kotlin洼专? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,163評(píng)論 9 118
  • 意境, 方為四大皆空蜡镶, 處處精致雾袱, 一絲淡泊與寧?kù)o的心境。 禪意空間 云水禪心官还,是自我的修行芹橡; 杯盞人生,是靈魂的...
    谷豐書畫陶刻藝術(shù)論壇閱讀 473評(píng)論 0 0
  • 不愿意與人分享快樂(lè)的事 源自于不愿與人分享痛苦的事 首先我相信一點(diǎn) 世界上沒(méi)有感同身受 同情對(duì)于沒(méi)經(jīng)歷過(guò)的人很容易...
    LUPOU閱讀 1,300評(píng)論 0 0