javascript
Spring5参考指南: SpEL
文章目錄
- Bean定義中的使用
- 求值
- 支持的功能
- 函數(shù)
- Bean引用
- If-Then-Else
- Elvis
- Safe Navigation 運(yùn)算符
- 集合選擇
- 集合投影
- 表達(dá)式模板化
SpEL的全稱叫做Spring Expression Language。通常是為了在XML或者注解里面方便求值用的,通過編寫#{ }這樣的格式,即可使用。
Bean定義中的使用
XML配置
可以用SpEL設(shè)置屬性或構(gòu)造函數(shù)參數(shù)值,如下示例所示:
<bean id="numberGuess" class="com.flydean.beans.NumberGuess"><property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/><!-- other properties --></bean>Spring內(nèi)置了很多預(yù)定義變量,如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>注解配置
要指定默認(rèn)值,可以將@value注解放在字段、方法、方法或構(gòu)造函數(shù)參數(shù)上。
以下示例設(shè)置字段變量的默認(rèn)值:
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;}}下面的示例顯示了在屬性設(shè)置器方法上的示例:
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方法和構(gòu)造函數(shù)也可以使用@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獨(dú)立使用的,這時(shí)候需要自己去創(chuàng)建一些引導(dǎo)基礎(chǔ)結(jié)構(gòu)類,如解析器。 大多數(shù)Spring用戶不需要處理這個(gè)基礎(chǔ)結(jié)構(gòu),只需要編寫表達(dá)式字符串進(jìn)行求值。
支持的功能
SpELl支持很多種功能,包括:
- 文字表達(dá)式
- 屬性、數(shù)組、列表、映射和索引器
- 內(nèi)聯(lián) List
- 內(nèi)聯(lián) Map
- Array
- 方法
- Operators
- 類型
- Constructors
- 變量
- 功能
- bean引用
- 三元運(yùn)算符(if-then-else)
- elvis
- Safe Navigation Operator
下面分別舉例子:
文字表達(dá)式
支持的文本表達(dá)式類型包括字符串、數(shù)值(int、real、hex)、布爾值和null。字符串由單引號分隔。要將單引號本身放入字符串中,請使用兩個(gè)單引號字符。
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);屬性名稱的第一個(gè)字母允許不區(qū)分大小寫。數(shù)組和列表的內(nèi)容是使用方括號表示法獲得的,如下例所示
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);映射的內(nèi)容是通過在括號內(nèi)指定文本鍵值獲得的:
// 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
你可以直接在表達(dá)式中表示列表:
// 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表示法在表達(dá)式中直接表示映射。以下示例顯示了如何執(zhí)行此操作:
// 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);構(gòu)造數(shù)組
可以使用熟悉的Java語法構(gòu)建數(shù)組,可以選擇的提供初始化器,以便在構(gòu)建時(shí)填充數(shù)組。以下示例顯示了如何執(zhí)行此操作:
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編程語法來調(diào)用方法。還可以對文本調(diào)用方法。還支持變量參數(shù)。以下示例演示如何調(diào)用方法:
// 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運(yùn)算符來指定java.lang.class(類型)的實(shí)例。靜態(tài)方法也可以使用此運(yùn)算符調(diào)用。StandardEvaluationContext使用TypeLocator來查找類型,StandardTypeLocator(可以替換)是在理解java.lang包的基礎(chǔ)上構(gòu)建的。這意味著T()對java.lang中類型的引用不需要完全限定,但所有其他類型引用都必須是限定的。下面的示例演示如何使用T運(yùn)算符:
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)造器
可以使用new運(yùn)算符調(diào)用構(gòu)造函數(shù)。除了基元類型(int、float等)和字符串之外,其他類型都應(yīng)該使用完全限定的類名。下面的示例演示如何使用新的運(yùn)算符來調(diào)用構(gòu)造函數(shù):
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語法引用表達(dá)式中的變量。變量是通過在EvaluationContext實(shí)現(xiàn)上使用setVariable方法設(shè)置的。以下示例顯示如何使用變量:
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始終是定義的,并引用當(dāng)前的評估對象。#root變量總是被定義并引用根上下文對象。盡管#this可能會隨著表達(dá)式的組件的計(jì)算而變化,但是#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);函數(shù)
您可以通過注冊可以在表達(dá)式字符串中調(diào)用的用戶定義函數(shù)來擴(kuò)展spel。該函數(shù)通過EvaluationContext注冊。以下示例顯示如何注冊用戶定義函數(shù):
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配置了評估上下文,則可以使用@符號從表達(dá)式中查找bean。以下示例顯示了如何執(zhí)行此操作:
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本身,您應(yīng)該在bean名稱前面加上&符號。以下示例顯示了如何執(zhí)行此操作:
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
可以使用三元運(yùn)算符在表達(dá)式中執(zhí)行if-then-else條件邏輯。下面的列表顯示了一個(gè)最小的示例:
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);Elvis
ELVIS運(yùn)算符是三元運(yùn)算符語法的縮寫,在groovy語言中使用。對于三元運(yùn)算符語法,通常必須重復(fù)變量兩次,如下示例所示:
String name = "Elvis Presley"; String displayName = (name != null ? name : "Unknown");相反,您可以使用Elvis操作符(以Elvis的發(fā)型命名)。下面的示例演示如何使用Elvis運(yùn)算符:
ExpressionParser parser = new SpelExpressionParser();String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown'Safe Navigation 運(yùn)算符
Safe Navigation操作符用于避免nullpointerException,它來自groovy語言。通常,當(dāng)您引用一個(gè)對象時(shí),您可能需要在訪問該對象的方法或?qū)傩灾膀?yàn)證它不是空的。為了避免這種情況,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是一種功能強(qiáng)大的表達(dá)式語言功能,通過從源集合的條目中進(jìn)行選擇,可以將源集合轉(zhuǎn)換為另一個(gè)集合。
Selection使用的語法為.?[selectionExpression]。它過濾集合并返回包含原始元素子集的新集合。例如,selection可以讓我們很容易地獲得塞爾維亞發(fā)明家的列表,如下示例所示:
在list和map上都可以Selection。對于list,將根據(jù)每個(gè)單獨(dú)的列表元素評估選擇條件。針對map,選擇標(biāo)準(zhǔn)針對每個(gè)映射條目(Java類型Map.Entry)進(jìn)行評估。每個(gè)map項(xiàng)都有其鍵和值,可以作為屬性訪問,以便在選擇中使用。
以下表達(dá)式返回一個(gè)新map,該映射由原始map的那些元素組成,其中輸入值小于27:
除了返回所有選定的元素之外,您還能檢索第一個(gè)或最后一個(gè)值。要獲取與所選內(nèi)容匹配的第一個(gè)條目,語法為。.^ [selectionExpression]。要獲取最后一個(gè)匹配的選擇,語法為.$[SelectionExpression]。
集合投影
Projection允許集合驅(qū)動子表達(dá)式的計(jì)算,結(jié)果是一個(gè)新集合。投影的語法是.![projectionExpression]。例如,假設(shè)我們有一個(gè)發(fā)明家列表,但是想要他們出生的城市列表。實(shí)際上,我們想為發(fā)明家列表中的每個(gè)條目評估“placeofbirth.city”。下面的示例使用投影進(jìn)行此操作:
// returns ['Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");您還可以使用map來驅(qū)動投影,在這種情況下,投影表達(dá)式針對map中的每個(gè)條目(表示為Java Map.Entry)進(jìn)行評估??鏼ap投影的結(jié)果是一個(gè)列表,其中包含對每個(gè)map條目的投影表達(dá)式的計(jì)算。
表達(dá)式模板化
表達(dá)式模板允許將文本與一個(gè)或多個(gè)計(jì)算塊混合。每個(gè)評估塊都由您可以定義的前綴和后綴字符分隔。常見的選擇是使用#{ }作為分隔符,如下示例所示:
String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}",new TemplateParserContext()).getValue(String.class);// evaluates to "random number is 0.7038186818312008"字符串的計(jì)算方法是將文本“random number is”與計(jì)算#{ }分隔符內(nèi)表達(dá)式的結(jié)果(在本例中,是調(diào)用該random()方法的結(jié)果)連接起來。parseExpression()方法的第二個(gè)參數(shù)的類型為parserContext。ParserContext接口用于影響表達(dá)式的解析方式,以支持表達(dá)式模板化功能。TemplateParserContext的定義如下:
public class TemplateParserContext implements ParserContext {public String getExpressionPrefix() {return "#{";}public String getExpressionSuffix() {return "}";}public boolean isTemplate() {return true;} }本節(jié)的例子可以參考spel
更多精彩內(nèi)容且看:
- 區(qū)塊鏈從入門到放棄系列教程-涵蓋密碼學(xué),超級賬本,以太坊,Libra,比特幣等持續(xù)更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續(xù)更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續(xù)更新
- java程序員從小工到專家成神之路(2020版)-持續(xù)更新中,附詳細(xì)文章教程
更多教程請參考 flydean的博客
總結(jié)
以上是生活随笔為你收集整理的Spring5参考指南: SpEL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring5参考指南: BeanWra
- 下一篇: Spring5参考指南:SpringAO