Spring 3.0 RC1發佈,一些新特性很吸引人,看了一下Reference,順便翻譯了SpEL這節,水平有限,還望指教。
Spring 3.0 Reference:http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/index.html
Part III 核心技術
6.Spring 表達式語言(SpEL)
- 6.1 簡介
- 6.2 特性概覽
- 6.3 使用Spring Expression接口進行表達式求值
- 6.4 bean定義的表達式支持
- 6.5 語言參考
- 6.6 示例類
6.1 簡介
Spring表達式語言(簡稱SpEL)是一個支持運行時查詢和操作對象圖的強大的表達式語言。其語法類似於統一EL,但提供了額外特性,顯式方法調用和基本字符串模板函數。
同很多可用的Java 表達式語言相比,例如OGNL,MVEL和JBoss EL,SpEL的誕生是爲了給Spring社區提供一個可以給Spring目錄中所有產品提供單一良好支持的表達式語言。其語言特性由Spring目錄中的項目需求驅動,包括基於eclipse的SpringSource套件中的代碼補全工具需求。那就是說,SpEL是一個基於技術中立的API允許需要時與其他表達式語言集成。
SpEL作爲Spring目錄中表達式求值的基礎,它並不是直接依賴於Spring而是可以被獨立使用。爲了能夠自包含,本章中的許多示例把SpEL作爲一個獨立的表達式語言來使用。這就需要創建一些如解析器的引導基礎組件類。大多數Spring用戶只需要爲求值編寫表達式字符串而不需要關心這些基礎組件。一個典型的使用例子是集成SpEL和創建基於XML或註解的bean定義,見6.4 bean定義的表達式支持一節。
本章涵蓋了表達式語言的特性,API和語法。在很多地方,一個Inventor類和Invertor的Society類作爲表達式求值的目標對象。這些類聲明和操作它們使用的數據列在本章的末尾。
6.2 特性概覽
表達式語言支持下列功能:
字符表達式
布爾和關係操作符
正則表達式
類表達式
訪問properties,arrays,lists,maps
方法調用
關係操作符
賦值
調用構造器
三元操作符
變量
用戶自定義函數
集合投影
集合選擇
模板表達式
6.3 使用Spring Expression接口進行表達式求值
這一節介紹SpEL接口和其表達式語言的簡單實用。完整的語言參考參見語言參考一節。
下面的代碼使用SpEL API求字符表達式‘Hello World’的值。
message變量的值是最簡單的‘Hello World’。
最常使用的SpEL類和接口在包org.springframework.expression和其子包以及spel.support中。
ExpressionParser接口用來解析一個表達式字符串。在這個例子中,表達式串是一個被單引號包括標註的字符串。Expression接口用來求前面定義的表達式串的值。當調用parser.parseExpression和exp.getValue時分別可能拋出ParseException和EvaluationException異常。
SpEL支持一系列特性,例如方法調用,訪問屬性和調用構造器。
下面調用字符串文字的concat方法作爲方法調用的一個例子。
message的值爲‘Hello World!’。
下面的String屬性Bytes可以被調用作爲調用JavaBean屬性的一個例子。
SpEL使用標準的‘.’符號支持屬性嵌套和屬性設值,例如:prop1.prop2.prop3.
公共屬性也可以被訪問。
使用字符串構造器而不是字符串文字。
記住使用泛型方法public < T> T getValue(Class<T> desiredResultType)。使用這個方法省去了需要時對表達式的值顯式類型轉換。如果該值不能被轉換爲T或者使用已註冊的類型轉換器轉換則會拋出EvaluationException.
SpEL中更常見的用途是提供一個針對特定對象實例求值的表達式字符串。在下面的例子中,我們檢索一個Inventor類的實例的name屬性。
在最後一行,該字符串變量'name'將被設置爲“Nikola Tesla”。類StandardEvaluationContext是您可以指定對象的將被求值的“name”屬性。你可以重複使用相同的表達式,在求值上下文內設定一個新的根對象。表達式是使用反射求值。
Note:在單獨使用SpEL時,你需要創建一個解析器並提供一個求值上下文。但是更爲廣泛的應用僅僅提供一個SpEL表達式字符串作爲配置文件的一部分。例如爲Spring bean或Spring Web Flow定義。這種情況下,解析器,求值上下文,根對象和其他預定義變量都會被隱含創建。
作爲最後一個例子,使用前面例子中的Inventor對象中使用布爾操作符。
EvaluationContext接口
EvaluationContext接口用來求一個解析屬性,方法,域的表達式值以及幫助類型轉換。其即插即用實現StandardEvaluationContext使用反射機制來操作對象。爲獲得好的性能緩存java.lang.reflect的Method,Field,和Constructor實例。
該StandardEvaluationContext是你用來通過方法setRootObject指定求值根對象或傳遞根對象到構造器的接口。你還可以指定將在表達式中使用的方法setVariable和registerFunction的變量和函數。變量和函數使用的描述見語言參考部分的變量和函數。StandardEvaluationContext還是你可以註冊自定義ConstructorResolvers,MethodResolvers,和PropertyAccessors來擴展SpEL如何計算表達式。請參閱這些類的JavaDoc瞭解詳情
類型轉換
默認情況下,SpEL使用的轉換服務可在Spring的核心(org.springframework.core.convert.ConversionService)找到。這種轉換的服務由很多內建的爲通常轉換的轉換器構成,但也是可擴展的,這樣可以添加自定義類型之間的轉換。此外它的主要支持是它是泛型敏感的。這意味着,當處理表達式中的泛型時,SpEL將嘗試轉換來維持對所遇到的任何對象類型的正確性。
這是什麼在實踐中意味着什麼呢?比如賦值,用setValue()來設置List類型,實際是List<Boolean>。SpEL將會認識到,在List的元素需要在使用之前轉換爲布爾類型。一個簡單的例子:
6.4 bean定義的表達式支持
SpEL可以和Bean定義中基於XML或註解的元數據配置一起使用。兩種情況下的語法都是如#{ <expression string> }的形式。
基於XML的配置
下面一個屬性或者構造參數值可以使用表達式設值。
變量‘systemProperties’是預先定義的,所以你可以在表達式中如下使用,記住在這個上下文中你不必在預定義的變量前加#號。
你可以通過名字引用其他bean的屬性,如下:
基於註解的配置
@Value註解可以在域,方法和方法/構造器參數中使用來指定一個默認值。
這是一個設置域變量默認值的例子。
和下面在屬性的setter方法中使用等價。
自動裝配的方法和構造器同樣可以使用@Value註解。
6.5 語言參考
文字表達式
支持的文字表達的類型是字符串,日期,數值(整型,實型,和十六進制),布爾和空。字符串是由單引號分隔。使用反斜槓字符轉移把一個單引號字符本身放在字符串中。以下清單顯示文字的簡單用法。通常它們不會使用這樣單獨使用,而是作爲一個更復雜的表達式的一部分,例如,使用一個文字表達式作爲邏輯比較運算符的一邊。
數字支持負號,指數符號的使用和小數點。默認情況下實數解析使用Double.parseDouble()。
Properties,Arrays,Lists,Maps,Indexers
用屬性引用導航很簡單,只需使用一個句點來表示一個嵌套的屬性值。Inventor類實例,pupin和tesla,使用示例中使用的類一節中列出的數據填充。要“向下”導航並獲得tesla的出生年份和pupin的出生的城市則使用下列表達式。
屬性名的首字母區分大小寫,數組和列表的內容使用方括號符號得到。
Map的內容通過指定括號內的文字鍵值得到。在這種情況下,因爲Officers Map的鍵是字符串,我們可以指定字符串。
方法
方法調用採用典型的Java編程語法。您還可以調用文字的方法。可變參數也支持。
操作符
關係操作符
使用標準的操作符號支持關係操作符:等於,不等於,小於,小於等於,大於,大於等於。
除此之外,SpEL支持‘instanceof’和基於正則表達式的‘match’操作。
//evaluatestofalse
boolean falseValue=parser.parseExpression("'xyz'instanceofT(int)").getValue(Boolean.class);
//evaluatestotrue
boolean trueValue=
parser.parseExpression("'5.00'matches'^-?//d+(//.//d{2})?$'").getValue(Boolean.class);
//evaluatestofalse
boolean falseValue=
parser.parseExpression("'5.0067'matches'^-?//d+(//.//d{2})?$'").getValue(Boolean.class);
邏輯操作符
支持的邏輯操作符包括and,or和not,使用方法如下。
算術操作符
加法運算符可以用於數字,字符串和日期。減法可用於數字和日期。乘法和除法僅可以用於。其他支持的數學運算包括取模(%)和指數冪(^)。使用標準的運算符優先級。這些運算符示例如下。
賦值
屬性設置是通過使用賦值運算符。這通常是在調用setValue中執行但也可以在調用getValue內。
類型
特殊的‘T'操作符可以用來指定一個java.lang.Class的實例('類型')。靜態方法調用也使用此操作符。該StandardEvaluationContext使用TypeLocator尋找類型,StandardTypeLocator(可更換)建立在java.lang包的基礎上。這意味着T()引用在java.lang中的類型不須被完全限定,但所有其他類型的引用必須。
構造器
可以使用new運算符調用構造器。完全限定類名應被用於所有類型除了原始類型和字符串(如整型,浮點,等等,可以使用)。
變量
變量可以在表達式中使用語法#’變量名’引用。變量設置使用StandardEvaluationContext的方法setVariable。
#this變量
#this變量通常被定義和引用當前求值對象(該對象的不合格引用將解決)。
函數
你可以通過註冊可在表達式字符串內調用的用戶自定義函數來擴展SpEL。使用StandardEvaluationContext中的下列方法註冊函數。
所引用的Java方法實現該函數,例如如下這個有用的反轉字符串方法。
隨後這個方法就可以在求值上下文註冊並在表達式字符串中使用了。
三元操作符(If-Then-Else)
可以在表達式內使用if-then-else條件邏輯三元操作符。下面是個小例子:
這個情況下,布爾false結果返回‘falseExp‘字符串,一個真實的例子如下:
下一節將看到三元操作符更短的語法Elvis操作符。
Elvis操作符
ElvIs操作符是Groovy語言中使用的三元操作符的縮短。在三元運算符中通常要重複變量兩次,例如:
可以使用Elvis操作符替代,命名爲和Elvis相似的風格。
這裏是一個複雜的例子。
安全導航操作符
安全導航操作符來源於Groovy語言,它避免了空指針異常。通常當你有一個對象的引用,在訪問其方法或屬性時你需要驗證該引用是否爲空。爲了避免這種情況,安全導航操作符,只會返回null而不是拋出異常。
集合選擇
選擇是一個強大的表達式語言特性,它允許你通過從入口選擇將原集合轉換爲其他集合。
選擇使用語法?[selectExpression]。這將過濾原集合並返回包含原集合子集的新的集合。例如,選擇將允許我們很容易得到Serbian 發明者列表。
通常是對List和Map的選擇。在前一種情況下的選擇標準是針對每個人的列表求值而對Map選擇標準是針對每一個映射項(Java類型Map.Entry對象)求值,而評價。Map入口有其如屬性的可訪問的鍵和值供選擇使用。
這個表達式將返回一個新的Map,其元素由原始Map中項的值小於27的元素組成。
除了返回所有被選擇的元素,也可以檢索第一個和最後一個值。用^[…]獲得第一個匹配入口,用$[…]獲得最後一個匹配入口。
集合投影
投影允許集合驅動子表達式的求值,其結果是一個新的集合。對於投影語法![projectionExpression]。最容易理解的例子,假設我們有一個發明者列表,但要在城市名單中尋找他們在那裏出生。我們要對發明者列表中每項有效地求出‘placeOfBirth.city'。使用投影:
Map也可以用於驅動投影,這時投影表達式對Map中的每個項目求值(作爲一個Java Map.Entry表示)。跨越Map投影的結果是對每個Map入口投影表達式求值後組成的列表。
表達式模板
表達式模板允許與一個或多個求值塊複合文字文本。每個求值塊有可自定義的前綴和後綴的字符定界,一個普遍的選擇是使用的分隔符$()定界。例如,
該字符串的求值是通過連接文字文本的‘random number is’和$()定界符內求值表達式的結果,在這裏是調用的random()方法的結果。parseExpression()方法的第二個參數是ParserContext的類型。該ParserContext接口通常影響表達式如何被解析以支持模板功能。在TemplatedParserContext的定義如下所示。
6.6 示例中使用的類
Inventor.java
PlaceOfBirth.java
Society.java