javascript
Spring5参考指南: SpEL
文章目錄
- Bean定義中的使用
- 求值
- 支持的功能
- 函數
- Bean引用
- If-Then-Else
- Elvis
- Safe Navigation 運算符
- 集合選擇
- 集合投影
- 表達式模板化
SpEL的全稱叫做Spring Expression Language。通常是為了在XML或者注解里面方便求值用的,通過編寫#{ }這樣的格式,即可使用。
Bean定義中的使用
XML配置
可以用SpEL設置屬性或構造函數參數值,如下示例所示:
<bean id="numberGuess" class="com.flydean.beans.NumberGuess"><property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/><!-- other properties --></bean>Spring內置了很多預定義變量,如SystemProperties, 你可以像下面這樣直接引用它:
<bean id="taxCalculator" class="com.flydean.beans.TaxCalculator"><property name="defaultLocale" value="#{ systemProperties['user.region'] }"/><!-- other properties --></bean>同樣的,你還可以按名稱引用其他bean屬性,如下示例所示:
<bean id="shapeGuess" class="com.flydean.beans.NumberGuess"><property name="randomNumber" value="#{ numberGuess.randomNumber }"/><!-- other properties --></bean>注解配置
要指定默認值,可以將@value注解放在字段、方法、方法或構造函數參數上。
以下示例設置字段變量的默認值:
public static class FieldValueTestBean@Value("#{ systemProperties['user.region'] }")private String defaultLocale;public void setDefaultLocale(String defaultLocale) {this.defaultLocale = defaultLocale;}public String getDefaultLocale() {return this.defaultLocale;}}下面的示例顯示了在屬性設置器方法上的示例:
public static class PropertyValueTestBeanprivate String defaultLocale;@Value("#{ systemProperties['user.region'] }")public void setDefaultLocale(String defaultLocale) {this.defaultLocale = defaultLocale;}public String getDefaultLocale() {return this.defaultLocale;}}autowired方法和構造函數也可以使用@value注解,如下示例所示:
public class SimpleMovieLister {private MovieFinder movieFinder;private String defaultLocale;@Autowiredpublic void configure(MovieFinder movieFinder,@Value("#{ systemProperties['user.region'] }") String defaultLocale) {this.movieFinder = movieFinder;this.defaultLocale = defaultLocale;}// ... } public class MovieRecommender {private String defaultLocale;private CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao,@Value("#{systemProperties['user.country']}") String defaultLocale) {this.customerPreferenceDao = customerPreferenceDao;this.defaultLocale = defaultLocale;}// ... }求值
雖然SpEL通常用在Spring的XML和注解中,但是它可以脫離Spring獨立使用的,這時候需要自己去創建一些引導基礎結構類,如解析器。 大多數Spring用戶不需要處理這個基礎結構,只需要編寫表達式字符串進行求值。
支持的功能
SpELl支持很多種功能,包括:
- 文字表達式
- 屬性、數組、列表、映射和索引器
- 內聯 List
- 內聯 Map
- Array
- 方法
- Operators
- 類型
- Constructors
- 變量
- 功能
- bean引用
- 三元運算符(if-then-else)
- elvis
- Safe Navigation Operator
下面分別舉例子:
文字表達式
支持的文本表達式類型包括字符串、數值(int、real、hex)、布爾值和null。字符串由單引號分隔。要將單引號本身放入字符串中,請使用兩個單引號字符。
public class LiteralApp {public static void main(String[] args) {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 2147483647int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();boolean trueValue = (Boolean) parser.parseExpression("true").getValue();Object nullValue = parser.parseExpression("null").getValue();} }Properties, Arrays, Lists, Maps, and Indexers
Properties 通過 “.” 來訪問嵌套的屬性值。如下:
// evals to 1856 int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);屬性名稱的第一個字母允許不區分大小寫。數組和列表的內容是使用方括號表示法獲得的,如下例所示
ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();// Inventions Array// evaluates to "Induction motor" String invention = parser.parseExpression("inventions[3]").getValue(context, tesla, String.class);// Members List// evaluates to "Nikola Tesla" String name = parser.parseExpression("Members[0].Name").getValue(context, ieee, String.class);// List and Array navigation // evaluates to "Wireless communication" String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(context, ieee, String.class);映射的內容是通過在括號內指定文本鍵值獲得的:
// Officer's DictionaryInventor 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");Inline List
你可以直接在表達式中表示列表:
// evaluates to a Java list containing the four numbers List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);Inline Map
你還可以使用key:value表示法在表達式中直接表示映射。以下示例顯示了如何執行此操作:
// evaluates to a Java map containing the two entries 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);構造數組
可以使用熟悉的Java語法構建數組,可以選擇的提供初始化器,以便在構建時填充數組。以下示例顯示了如何執行此操作:
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 bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);// evaluates to true boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);類型
您可以使用特殊的T運算符來指定java.lang.class(類型)的實例。靜態方法也可以使用此運算符調用。StandardEvaluationContext使用TypeLocator來查找類型,StandardTypeLocator(可以替換)是在理解java.lang包的基礎上構建的。這意味著T()對java.lang中類型的引用不需要完全限定,但所有其他類型引用都必須是限定的。下面的示例演示如何使用T運算符:
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);構造器
可以使用new運算符調用構造函數。除了基元類型(int、float等)和字符串之外,其他類型都應該使用完全限定的類名。下面的示例演示如何使用新的運算符來調用構造函數:
Inventor einstein = p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')").getValue(Inventor.class);//create new inventor instance within add method of List p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))").getValue(societyContext);變量
可以使用#variableName語法引用表達式中的變量。變量是通過在EvaluationContext實現上使用setVariable方法設置的。以下示例顯示如何使用變量:
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); context.setVariable("newName", "Mike Tesla");parser.parseExpression("Name = #newName").getValue(context, tesla); System.out.println(tesla.getName()) // "Mike Tesla"#this和#root
#this始終是定義的,并引用當前的評估對象。#root變量總是被定義并引用根上下文對象。盡管#this可能會隨著表達式的組件的計算而變化,但是#root始終引用根。以下示例說明如何使用#this和#root變量:
// 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(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess(); 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);函數
您可以通過注冊可以在表達式字符串中調用的用戶定義函數來擴展spel。該函數通過EvaluationContext注冊。以下示例顯示如何注冊用戶定義函數:
public abstract class StringUtils {public static String reverseString(String input) {StringBuilder backwards = new StringBuilder(input.length());for (int i = 0; i < input.length(); i++)backwards.append(input.charAt(input.length() - 1 - i));}return backwards.toString();} } ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("reverseString",StringUtils.class.getDeclaredMethod("reverseString", String.class));String helloWorldReversed = parser.parseExpression("#reverseString('hello')").getValue(context, String.class);Bean引用
如果已使用bean resolver配置了評估上下文,則可以使用@符號從表達式中查找bean。以下示例顯示了如何執行此操作:
ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver());// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation Object bean = parser.parseExpression("@something").getValue(context);要訪問工廠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);If-Then-Else
可以使用三元運算符在表達式中執行if-then-else條件邏輯。下面的列表顯示了一個最小的示例:
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);Elvis
ELVIS運算符是三元運算符語法的縮寫,在groovy語言中使用。對于三元運算符語法,通常必須重復變量兩次,如下示例所示:
String name = "Elvis Presley"; String displayName = (name != null ? name : "Unknown");相反,您可以使用Elvis操作符(以Elvis的發型命名)。下面的示例演示如何使用Elvis運算符:
ExpressionParser parser = new SpelExpressionParser();String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown'Safe Navigation 運算符
Safe Navigation操作符用于避免nullpointerException,它來自groovy語言。通常,當您引用一個對象時,您可能需要在訪問該對象的方法或屬性之前驗證它不是空的。為了避免這種情況,Safe Navigation操作符返回空值而不是拋出異常。以下示例說明如何使用Safe Navigation:
ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); System.out.println(city); // Smiljantesla.setPlaceOfBirth(null); city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); System.out.println(city); // null - does not throw NullPointerException!!!集合選擇
Selection是一種功能強大的表達式語言功能,通過從源集合的條目中進行選擇,可以將源集合轉換為另一個集合。
Selection使用的語法為.?[selectionExpression]。它過濾集合并返回包含原始元素子集的新集合。例如,selection可以讓我們很容易地獲得塞爾維亞發明家的列表,如下示例所示:
在list和map上都可以Selection。對于list,將根據每個單獨的列表元素評估選擇條件。針對map,選擇標準針對每個映射條目(Java類型Map.Entry)進行評估。每個map項都有其鍵和值,可以作為屬性訪問,以便在選擇中使用。
以下表達式返回一個新map,該映射由原始map的那些元素組成,其中輸入值小于27:
除了返回所有選定的元素之外,您還能檢索第一個或最后一個值。要獲取與所選內容匹配的第一個條目,語法為。.^ [selectionExpression]。要獲取最后一個匹配的選擇,語法為.$[SelectionExpression]。
集合投影
Projection允許集合驅動子表達式的計算,結果是一個新集合。投影的語法是.![projectionExpression]。例如,假設我們有一個發明家列表,但是想要他們出生的城市列表。實際上,我們想為發明家列表中的每個條目評估“placeofbirth.city”。下面的示例使用投影進行此操作:
// returns ['Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");您還可以使用map來驅動投影,在這種情況下,投影表達式針對map中的每個條目(表示為Java Map.Entry)進行評估。跨map投影的結果是一個列表,其中包含對每個map條目的投影表達式的計算。
表達式模板化
表達式模板允許將文本與一個或多個計算塊混合。每個評估塊都由您可以定義的前綴和后綴字符分隔。常見的選擇是使用#{ }作為分隔符,如下示例所示:
String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}",new TemplateParserContext()).getValue(String.class);// evaluates to "random number is 0.7038186818312008"字符串的計算方法是將文本“random number is”與計算#{ }分隔符內表達式的結果(在本例中,是調用該random()方法的結果)連接起來。parseExpression()方法的第二個參數的類型為parserContext。ParserContext接口用于影響表達式的解析方式,以支持表達式模板化功能。TemplateParserContext的定義如下:
public class TemplateParserContext implements ParserContext {public String getExpressionPrefix() {return "#{";}public String getExpressionSuffix() {return "}";}public boolean isTemplate() {return true;} }本節的例子可以參考spel
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
更多教程請參考 flydean的博客
總結
以上是生活随笔為你收集整理的Spring5参考指南: SpEL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring5参考指南: BeanWra
- 下一篇: Spring5参考指南:SpringAO