Spring EL 簡介

本文參考自Spring官方文檔 Spring EL

在Java上有很多表達(dá)式語言,在很多領(lǐng)域有各種各樣的應(yīng)用。我們應(yīng)該很熟悉Java EE的表達(dá)式語言吧,讓我們能在JSP中隨意插入數(shù)據(jù)。Spring也提供了一個(gè)表達(dá)式語言并添加了自己的功能,以便可以方便的和各種Spring框架交互。我們在項(xiàng)目中不需要手動(dòng)管理Spring表達(dá)式的這些接口和實(shí)例,只需要在合適的時(shí)候編寫Spring表達(dá)式,轉(zhuǎn)換器就會(huì)自動(dòng)解析并轉(zhuǎn)換表達(dá)式。

創(chuàng)建和使用解析器

當(dāng)然,為了說明一下Spring表達(dá)式,我們在這里還是手動(dòng)創(chuàng)建一個(gè)解析器來解析表達(dá)式。下面是簡單的單元測試。

public class SpringElTest {
    private static ExpressionParser parser = new SpelExpressionParser();

    @Test
    public void testHelloWorld() {
        Expression expression = parser.parseExpression("'你好世界!'");
        String result = (String) expression.getValue();
        System.out.println(result);

    }
}

還可以使用更復(fù)雜的例子。

    @Test
    public void testStringOperation() {
        Expression expression = parser.parseExpression("'你好'.concat('世界!')");
        String result = (String) expression.getValue();
        System.out.println(result);
        expression = parser.parseExpression("'Hello world!'.toUpperCase()");
        result = expression.getValue(String.class);
        System.out.println(result);
    }

Spring文檔解釋了如何創(chuàng)建和使用Spring表達(dá)式的各個(gè)接口、編譯和配置等等。但是一般情況下我們用不到這些功能。這里就只介紹一下Spring El的語法。如果需要詳細(xì)了解這些信息的話還是直接看文檔吧。

語言指南

這部分介紹了Spring EL表達(dá)式的使用。為了省事就直接引用了文檔的代碼了。下面這些代碼沒有說明的話都是Spring文檔的例子。

字面值

表達(dá)式支持各種類型的字面值。字符串字面值需要使用單引號包括,其他類型字面值直接寫就行。

ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

屬性和集合

Spring表達(dá)式支持屬性,只要使用點(diǎn)號引用屬性即可。

int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);

String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);

數(shù)組和列表可以使用方括號語法引用對應(yīng)索引的元素。

ExpressionParser parser = new SpelExpressionParser();

// Inventions Array
StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
        teslaContext, String.class);

// Members List
StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee);

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(
        societyContext, String.class);

// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
        societyContext, String.class);

Map類型也可以使用方括號語法引用鍵對應(yīng)的值。

Inventor pupin = parser.parseExpression("Officers['president']").getValue(
        societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(
        societyContext, String.class);

// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
        societyContext, "Croatia");

內(nèi)聯(lián)集合

我們可以直接在表達(dá)式中定義集合,這就是內(nèi)聯(lián)。內(nèi)聯(lián)集合使用花括號語法。

List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);

List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

內(nèi)聯(lián)Map則要復(fù)雜一點(diǎn),使用類似JSON的語法,鍵和值之間用冒號分隔開。

Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

數(shù)組

數(shù)組使用類似Java的語法,可以給出初始值,多維數(shù)組也受支持。

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

方法

方法和Java語法一樣。

// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);

運(yùn)算符

表達(dá)式中支持各種運(yùn)算符,運(yùn)算規(guī)則和Java規(guī)則類似。唯一需要注意的是空值的處理,假設(shè)有非空值val,那么下面的表達(dá)式恒為真:val > null。這一點(diǎn)需要注意。

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

邏輯運(yùn)算符可以使用andor!。

類型

特殊的T運(yùn)算符可以獲取表達(dá)式對象的類型。

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);

boolean trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class);

構(gòu)造器

在表達(dá)式中,使用new關(guān)鍵字來調(diào)用構(gòu)造器。

Inventor einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor.class);

變量

在表達(dá)式上下文中,我們可以設(shè)置新變量。然后在表達(dá)式中使用#變量名訪問變量。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("Name = #newName").getValue(context);

System.out.println(tesla.getName()) // "Mike Tesla"

#this和#root

#this#root代表了表達(dá)式上下文的對象,#root就是當(dāng)前的表達(dá)式上下文對象,#this則根據(jù)當(dāng)前求值環(huán)境的不同而變化。下面的例子中,#this即每次循環(huán)的值。

// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes",primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);

Bean引用

這是Spring表達(dá)式獨(dú)有的功能,我們可以在表達(dá)式中引用配置文件定義的其他Bean,這需要語法@Bean名稱

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);

如果需要獲取Bean工廠本身而不是它構(gòu)造的Bean,可以使用&Bean名稱。

Object bean = parser.parseExpression("&foo").getValue(context);

三元運(yùn)算符

和Java的三元運(yùn)算符類似。

String falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String.class);

Elvis運(yùn)算符

在一些編程語言中(比如C#、Kotlin等)提供該功能,語法是?:。意義是當(dāng)某變量不為空的時(shí)候使用該變量,當(dāng)該變量為空的時(shí)候使用指定的默認(rèn)值。

ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("name?:'Unknown'").getValue(String.class);

System.out.println(name); // 'Unknown'

安全導(dǎo)航運(yùn)算符

這是來自Groovy的一個(gè)功能,語法是?.,當(dāng)然有些語言也提供了這個(gè)功能。當(dāng)我們對對象的某個(gè)屬性求值時(shí),如果該對象本身為空,就會(huì)拋出空指針異常,如果使用安全導(dǎo)航運(yùn)算符,空對象的屬性就會(huì)簡單的返回空。

city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);

System.out.println(city); // null - does not throw NullPointerException!!!

集合選擇

這有點(diǎn)類似Java 8的Filter流方法,作用是選擇或者說是過濾,語法是集合對象.?[選擇表達(dá)式],Spring會(huì)迭代集合對象的每一個(gè)元素,并使用選擇表達(dá)式判斷該元素是否滿足條件,最后返回由滿足條件的元素組成的新集合。下面的例子就返回了值大于27的新Map。

Map newMap = parser.parseExpression("map.?[value<27]").getValue();

集合投影

這類似Java 8的Map流方法或者SQL語言的選擇語句,作用是將一個(gè)集合中所有元素的某屬性抽取出來,組成一個(gè)新集合。語法是![投影表達(dá)式]。下面的例子選出了由Member的placeOfBirth的city屬性組成的新集合。

List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");

表達(dá)式模板

表達(dá)式模板使用#{}定義,它允許我們混合多種結(jié)果。下面就是一個(gè)例子,首先Spring會(huì)先對模板中的表達(dá)式求值,在這里是返回一個(gè)隨機(jī)值,然后將結(jié)果和外部的表達(dá)式組合起來。最終的結(jié)果就向下面這樣了。

String randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        new TemplateParserContext()).getValue(String.class);
// 結(jié)果是 "random number is 0.7038186818312008"

如果表達(dá)式只是一個(gè)簡單的表達(dá)式,就不需要使用模板。只有表達(dá)式有很多表達(dá)式組成時(shí)才需要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,275評論 6 342
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,728評論 18 399
  • 若我還能再去愛一個(gè)人。 還記得你說話的溫度,還記得你指尖的硬度,還記得你眼神的味道,還記得你行...
    徐來K閱讀 288評論 0 0
  • 一個(gè)比較有意思的說法,若是某件事兒你覺得需要努力、需要堅(jiān)持才行,那這事兒基本上從一開始就注定做不成了……需要努力、...
    汪超_1362閱讀 194評論 0 0

友情鏈接更多精彩內(nèi)容