javascript
Spring--SPeL
文章目錄
- 介紹
- Feature
- 表達式計算
- package:
- `示例:`
- Parser configuration
- SpEL compilation
- **支持定義bean definition的表達式**
- 基于XML配置
- 基于注解的配置
- **Language Reference**
- **字面值表達式 (literal expression)**
- **Properties、Arrays、Lists、Maps、Indexers**
- **內(nèi)聯(lián)lists(inline lists)**
- **內(nèi)聯(lián)maps** (**inline maps** )
- 構(gòu)建數(shù)組
- 方法調(diào)用
- **操作符(Operators )**
- **關(guān)系操作符 (Relational operators)**
- **邏輯操作符(Logical operators)**
- **數(shù)學(xué)操作符(Mathematical operators)**
- **賦值(Assignment)**
- **類型(Types)**
- **構(gòu)造器(Constructors )**
- **變量(Variables )**
- **#this、#root**
- **Functions**
- **Bean references**
- 三元操作符(Ternary Operator (If-Then-Else))
- **Elvis operator**
- **安全導(dǎo)航操作符(Safe Navigation operator )**
- **集合選擇(Collection Selection)**
- **集合投影(Collection Projection )**
- **表達式模板化(Expression templating )**
- EvaluationContext
- 完整示例
- 參考:
介紹
spring-expression(SPeL) 模塊提供了一種強大的表達式語言,用來在運行時查詢和操作對象圖。它是作為JSP 2.1 規(guī)范所指定的統(tǒng)一表達式語言的一種擴展。這種語言支持對屬性值、屬性參數(shù)、方法調(diào)用、數(shù)組內(nèi)容存儲、收集器和索引、邏輯和算數(shù)的操作及命名變量, 并且通過名稱從Spring的控制反轉(zhuǎn)容器中取回對象。表達式語言模塊還支持列表投影、選擇和通用列表聚合。
Feature
支持的功能包括:
- Literal expressions (字面量表達式)
- Boolean and relational operators (布爾和關(guān)系操作符)
- Regular expressions (正則表達式)
- Class expressions (類表達式)
- Accessing properties, arrays, lists, maps(屬性,數(shù)組,List,MAP)
- Method invocation(方法調(diào)用)
- Relational operators(關(guān)系操作符)
- Assignment(賦值)
- Calling constructors(構(gòu)造函數(shù))
- Bean references(Bean引用)
- Array construction(構(gòu)造數(shù)組)
- Inline lists(內(nèi)聯(lián)lists)
- Inline maps(內(nèi)聯(lián)maps)
- Ternary operator(三元操作符)
- Variables(變量)
- User defined functions(自定義函數(shù))
- Collection projection(集合投影)
- Collection selection(集合選擇)
- Templated expressions(模板化表達式)
表達式計算
package:
- org.springframework.expression
- org.springframework.expression.spel.support
示例:
ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'.concat('!')"); String message = (String) exp.getValue();使用更多的是,表達式應(yīng)用在一個對象實例上(root Object),StandardEvaluationContext利用了反射來操作對象、緩存 java.lang.reflect.Method、java.lang.reflect.Field和java.lang.reflect.Constructor的實例以增進性能。
可以指定目標(biāo)對象(root object):使用無參構(gòu)造,再調(diào)用setRootObject();或者,使用有參構(gòu)造。
也可以指定Expression中使用的變量和方法:setVariable()、registerFunction()。
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("name");EvaluationContext context = new StandardEvaluationContext(tesla); String name = (String) exp.getValue(context);Expression exp = parser.parseExpression("name == 'Nikola Tesla'"); boolean result = exp.getValue(context, Boolean.class); // evaluates to trueParser configuration
可以為Parser指定Parser configuration對象(org.springframework.expression.spel.**SpelParserConfiguration**),用于控制一些組件的解析行為。典型的,例如 通過索引訪問數(shù)組或集合,如果返回的是null,可以讓它自動創(chuàng)建元素(empty element)。
class Demo {public List<String> list; }// Turn on: // true- 值為null,則自動初始化一個對象 // true- 自動增長capcity SpelParserConfiguration config = new SpelParserConfiguration(true,true);ExpressionParser parser = new SpelExpressionParser(config);Expression expression = parser.parseExpression("list[3]");Demo demo = new Demo();Object o = expression.getValue(demo);// demo.list是長度為4的List,每個元素都為空字符串SpEL compilation
編譯能力增強
支持定義bean definition的表達式
SpEL可以用在基于XML或基于注解的配置元數(shù)據(jù)以定義BeanDefinition。語法: #{ <expression string> } 。
基于XML配置
設(shè)置property或者constructor-args:
<bean id="numberGuess" class="org.spring.samples.NumberGuess"><property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/><!-- other properties --> </bean> <bean id="person" class="cn.larry.demo.Person"><!-- 引用對象--><property name="name" value="#{ systemProperties['user.name'] }"/><!-- other properties --> </bean> <bean id="numberGuess" class="org.spring.samples.NumberGuess"><property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> </bean><!-- 引用bean:numberGuess 的 屬性:randomNumber --> <bean id="shapeGuess" class="org.spring.samples.ShapeGuess"><property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> </bean>基于注解的配置
@Value 注解可以放在fields、methods、method/constructor的參數(shù)上,以注入默認(rèn)值。
- 字段默認(rèn)值
- setter默認(rèn)值
- 方法參數(shù)默認(rèn)值
Language Reference
字面值表達式 (literal expression)
字面值表達式支持的類型包括strings、dates、numeric values(int、real、hex)、boolean 以及 null。
字符串使用單引號間隔。如要輸出單引號,兩個單引號即可。(轉(zhuǎn)義)
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();Properties、Arrays、Lists、Maps、Indexers
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class); Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext, Inventor.class);內(nèi)聯(lián)lists(inline lists)
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);//如果表達式是字面量的組合,則創(chuàng)建一個常量list,而不是每個部分一個值。 List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);內(nèi)聯(lián)maps (inline maps )
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context); //同list,內(nèi)嵌list,map,構(gòu)造成一個常量。 Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);構(gòu)建數(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);方法調(diào)用
// string literal, evaluates to "bc" String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);// evaluates to true boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);操作符(Operators )
關(guān)系操作符 (Relational operators)
==、!=、<、<=、>、>=
// 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);除了標(biāo)準(zhǔn)的關(guān)系操作符,SpEL還支持 instanceOf操作符 和 基于正則表達式的matches操作符!
// evaluates to false boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'5.00' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); //evaluates to false boolean falseValue = parser.parseExpression("'5.0067' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);邏輯操作符(Logical operators)
and、or、not
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);數(shù)學(xué)操作符(Mathematical operators)
+操作符可以用于numbers和strings。-*/只能用于numbers。
另外還支持%和^。優(yōu)先級同Java語法。
// Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0賦值(Assignment)
可以通過expression的setValue進行賦值,也可以在getValue內(nèi)部實現(xiàn)
Inventor inventor = new Inventor(); StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");// alternatively String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, String.class);類型(Types)
特殊的T操作符用于指定 java.lang下的類型。也可以通過該操作符調(diào)用靜態(tài)方法。
使用該操作符引用java.lang下的類型時,可以省略包路徑。但其他引用不能省略!
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)造器(Constructors )
使用 new操作符 可以調(diào)用構(gòu)造器。
此時,除了primitive 類型和String類型之外,其他所有類型都必須是全路徑。
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);變量(Variables )
可以引用變量,格式:#variableName。變量是使用StandardEvaluationContext的setVariable來設(shè)置的。如下:
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引用當(dāng)前對象,#root引用root object
// 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);Functions
通過注冊自己的Function來擴展SpEL!使用StandardEvaluationContext來注冊Function
public void registerFunction(String name, Method m) 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();} }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);Bean references
如果evaluation context配置了一個bean resolver,就可以使用**@**符號來查找beans!
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);三元操作符(Ternary Operator (If-Then-Else))
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);Elvis operator
是三目運算符的簡寫,用于groovy語言中。
通常,如果使用三目運算符,會重復(fù)一個變量兩次,例如:
ExpressionParser parser = new SpelExpressionParser(); String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown'安全導(dǎo)航操作符(Safe Navigation operator )
用于避免空指針異常,來自Groovy語言。
一般來說,當(dāng)你引用一個對象時,在訪問其property或method之前需要確認(rèn)非null。而安全導(dǎo)航操作符則簡單的返回一個null,而非拋出異常。
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);上面,這個**?**就是安全導(dǎo)航操作符,用在對象后面!!!
集合選擇(Collection Selection)
選擇是一個強大的表達式語言特色。
格式: ?[selectionExpression]。會過濾一個集合,返回一個新集合。如下:
List<Inventor> list = (List<Inventor>) parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext);上面的代碼,會返回Members集合中Nationality是Serbian的Inventor。
即可用于list也可以用于map。對list來說,是對每個元素進行選擇;對map來說,則是對每個鍵值對Map.Entry進行選擇。
Map newMap = parser.parseExpression("map.?[value<27]").getValue();除了返回選擇的元素之外,還可以直接返回第一個或最后一個:1和$[…]。
集合投影(Collection Projection )
格式:![projectionExpression]。
// returns ['Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");集合投影表達式,可以支持,數(shù)學(xué)計算,MAP
ExpressionParser parser = new SpelExpressionParser();StandardEvaluationContext context = new StandardEvaluationContext();Inventor a = new Inventor("a", "A");Inventor b = new Inventor("b", "B");Inventor c = new Inventor("c", "C");a.setPlaceOfBirth(new PlaceOfBirth("AA"));b.setPlaceOfBirth(new PlaceOfBirth("BB"));c.setPlaceOfBirth(new PlaceOfBirth("AA"));a.setMoney(50);b.setMoney(60);c.setMoney(70);Society aa = new Society();aa.setName("alice");aa.getMembers().add(a);aa.getMembers().add(b);aa.getMembers().add(c);context.setRootObject(aa);context.setVariable("s", aa);List<Inventor> list0 = (List<Inventor>) parser.parseExpression("#s.Members").getValue(context);Object o = parser.parseExpression("#root").getValue(context);List<String> l = (List<String>) parser.parseExpression("#root.Members.![placeOfBirth.city]").getValue(context); //支持?jǐn)?shù)學(xué)運算List<Integer> l2 = (List<Integer>) parser.parseExpression("#root.Members.![#this.money * 30] ").getValue(context); //支持+Map<String, String> m = (Map<String, String>) parser.parseExpression("{name:#root.Members.![name],city:#root.Members.![placeOfBirth.city]}").getValue(context);List<String> m2 = (List<String>) parser.parseExpression("#root.Members.!['{name:' + #this.name + '}']").getValue(context); //支持投影為一個mapList< Map<String, String> > m3 = ( List< Map<String, String> > ) parser.parseExpression("#root.Members.![ {x:#this.name,y:#this.placeOfBirth.city}]").getValue(context);表達式模板化(Expression templating )
表達式模板允許將字符串字面值與一個或多個evaluation blocks混合起來。每個evaluation block的前后綴都可以自定義,但默認(rèn)使用 #{}。 如下:
String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}",new TemplateParserContext()).getValue(String.class);// evaluates to "random number is 0.7038186818312008"關(guān)鍵是parseExpression()的第二個參數(shù),是ParserContext類型。ParserContext接口用來影響表達式如何被解析以便支持模板功能。
public class TemplateParserContext implements ParserContext {public String getExpressionPrefix() {return "#{";}public String getExpressionSuffix() {return "}";}public boolean isTemplate() {return true;} }EvaluationContext
EvaluationContext接口在解析一個涉及屬性,方法,字段或者類型轉(zhuǎn)換時使用。Spring提供了兩種實現(xiàn):
(1)SimpleEvaluationContext:為不需要SpEL語言語法的完整范圍的表達式類別公開一個基本SpEL語言功能和配置選項的子集,并且應(yīng)該進行有意義的限制。示例包括但不限于數(shù)據(jù)綁定表達式和基于屬性的過濾器。
SimpleEvaluationContext旨在僅支持SpEL語言語法的子集。它排除了Java類型引用,構(gòu)造函數(shù)和bean引用。它還要求您明確選擇表達式中屬性和方法的支持級別。默認(rèn)情況下,create()靜態(tài)工廠方法僅啟用對屬性的讀訪問權(quán)限。
(2)StandardEvaluationContext:公開全套SpEL語言功能和配置選項。您可以使用它來指定默認(rèn)根對象并配置每個可用的與評估相關(guān)的策略。
完整示例
package demon.study.spring5.spel;import java.util.ArrayList; import java.util.List;public class Person {private List<Work> works;public List<Work> getWorks() {return works;}String name;int age;public Person (){this.works = new ArrayList<Work>();}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public boolean isFemale() {return female;}public void setFemale(boolean female) {this.female = female;}boolean female;} package demon.study.spring5.spel;import java.util.Date;public class Work {private String companyName;private String position;private Date start;private Date end;public Work(String companyName,String position){this.companyName = companyName;this.position = position;}public String getCompanyName() {return companyName;}public void setCompanyName(String companyName) {this.companyName = companyName;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}public Date getStart() {return start;}public void setStart(Date start) {this.start = start;}public Date getEnd() {return end;}public void setEnd(Date end) {this.end = end;}} package demon.study.spring5.spel;import org.springframework.expression.spel.standard.SpelExpressionParser;import java.util.ArrayList; import java.util.List;import org.springframework.expression.*;public class SpelTest {public static void main(String[] args) {// TODO Auto-generated method stubtest();test2();}public static void test() {ExpressionParser parser = new SpelExpressionParser();String expValue = "'Hello word'";Expression exp = parser.parseExpression(expValue);Object expResult = exp.getValue();System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "0x77777777";exp = parser.parseExpression(expValue);expResult = exp.getValue();System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "true and false";exp = parser.parseExpression(expValue);expResult = exp.getValue();System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "{1,2,3,4}";exp = parser.parseExpression(expValue);expResult = exp.getValue();System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "{name:'bob',age:15}";exp = parser.parseExpression(expValue);expResult = exp.getValue();System.out.println("Parse [" + expValue + "] to :" + expResult);}public static void test2() {List<String> list = new ArrayList<String>();list.add("1");list.add("2");list.add("3");Person p = new Person();p.setName("bob");p.setAge(5);p.getWorks().add(new Work("baidu", "developer"));p.getWorks().add(new Work("ali", "developer"));p.getWorks().add(new Work("ali", "leader"));p.getWorks().add(new Work("tx", "leader"));ExpressionParser parser = new SpelExpressionParser();String expValue = "name";Expression exp = parser.parseExpression(expValue);Object expResult = exp.getValue(p, String.class);System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "works[1]";exp = parser.parseExpression(expValue);expResult = exp.getValue(p);System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "works.?[companyName=='ali']";exp = parser.parseExpression(expValue);expResult = exp.getValue(p);System.out.println("Parse [" + expValue + "] to :" + expResult);expValue = "works.![companyName]";exp = parser.parseExpression(expValue);expResult = exp.getValue(p);System.out.println("Parse [" + expValue + "] to :" + expResult);}}輸出:
Parse [‘Hello word’] to :Hello word
Parse [0x77777777] to :2004318071
Parse [true and false] to :false
Parse [{1,2,3,4}] to :[1, 2, 3, 4]
Parse [{name:‘bob’,age:15}] to :{name=bob, age=15}
Parse [name] to :bob
Parse [works[1]] to :demon.study.spring5.spel.Work@520dac39
Parse [works.?[companyName==‘a(chǎn)li’]] to :[demon.study.spring5.spel.Work@520dac39, demon.study.spring5.spel.Work@117379b8]
Parse [works.![companyName]] to :[baidu, ali, ali, tx]
參考:
https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html
… ??
總結(jié)
以上是生活随笔為你收集整理的Spring--SPeL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring--总体架构
- 下一篇: Maven(1)--坐标与依赖