一)什么是SpEL
SpEL -- Spring Expression Language. Spring的表达式语言。举个最简单的例子:
最后 message的值就是 Hello World, 表达式中的单引号''就是表达String类型的一种格式。另外值得注意的一点时,当表达式不符合规范时, parser.parseExpression语句会抛出ParseException;而exp.getValue会抛出EvaluationException
而为了避免最后的类型转换,我们还可以这样写:
二)SpEL举例中需要使用到的类
以下是本文介绍SpEL过程中需要使用到的类,在此列出,以供后续介绍中使用参考:
Inventor.java
PlaceOfBirth.java
Society.java
三)EvaluationContext接口
当在使用表达式的过程中有遇到类型转换时,我们需要EvaluationContext接口加以辅助:
注:EvaluationContext开销较大,所以多用于数据变化较少的情况。如果数据变化频繁,我们可以考虑直接引用对象(比如上例中的simple)以减小开销。
四)SpEL
下面正式开始介绍SpEL。
1'Literal expressions 基本类的表达式
2' Properties, Arrays, Lists, Maps, Indexers 属性、集合的表示
Properties:
arrays and lists:
Map:
3' construction
lists:
arrays:
4' Methods
5' Operators
Relational operators:
注意关键字:instanceof、matches
Logical operators:
Mathematical operators:
6' Assignment
7' Types
注: 在java.lang中的类不需要全路径。
8' Constructors
9' Variables
注: Spring还有两个预留的variables: #this #root #this表示集合中的当前元素,#root表示根对象,即载入context的对象。
10' Functions
Function的使用略微复杂。首先是调用StandardEvaluationContext的方法
进行注册,而后才能在expression中进行调用:
11'Bean references
没啥好多说的,知道用@就可以了~~
12'Ternary Operator
结果是: falseExp
13'The Elvis Operator
这是一种对三重操作符的简化写法。比如用三重操作符我们可以这样:
等效的用Elvis操作符实现的话就是这样:
省略了name的显示表达!
14'Safe Navigation operator
用?.取代. 当代码会抛出NullPointerException时,采用?.的操作仅仅会简单的返回一个null。
15' Collection Selection
如果你要在集合内挑选符合某些条件的元素,你可以采用这样格式的表达式?[selectionExpression]
而如果你只需要获得第一个满足条件的元素,你可以用:^[...]
而如果你只需要获得最后一个满足条件的元素,你可以用:$[...]
16' Collection Projection
那么我需要便利某些元素以组成新的集合,我们可以采用这样的表达式![projectionExpression]:
17' Expression templating
template的作用便是在表达式内再嵌套表达式。比如:
而要实现内部嵌套的T(java.lang.Math).random()表达式。我们需要实现接口ParserContext:
五)SpEL在配置中的使用
同样的语法,SpEL还能在配置文件和annotation中使用!!强大吧 ^.^ 它们的格式是#{ <expression string> }
在配置文件中:
在annotation中使用:
SpEL -- Spring Expression Language. Spring的表达式语言。举个最简单的例子:
- ExpressionParser parser =new SpelExpressionParser();
- Expression exp = parser.parseExpression("'Hello World'");
- String message = (String) exp.getValue();
最后 message的值就是 Hello World, 表达式中的单引号''就是表达String类型的一种格式。另外值得注意的一点时,当表达式不符合规范时, parser.parseExpression语句会抛出ParseException;而exp.getValue会抛出EvaluationException
而为了避免最后的类型转换,我们还可以这样写:
- String message = exp.getValue(String.class);
二)SpEL举例中需要使用到的类
以下是本文介绍SpEL过程中需要使用到的类,在此列出,以供后续介绍中使用参考:
Inventor.java
- package org.spring.samples.spel.inventor;
- import java.util.Date;
- import java.util.GregorianCalendar;
- public class Inventor {
- private String name;
- private String nationality;
- private String[] inventions;
- private Date birthdate;
- private PlaceOfBirth placeOfBirth;
- public Inventor(String name, String nationality){
- GregorianCalendar c= new GregorianCalendar();
- this.name = name;
- this.nationality = nationality;
- this.birthdate = c.getTime();
- }
- public Inventor(String name, Date birthdate, String nationality) {
- this.name = name;
- this.nationality = nationality;
- this.birthdate = birthdate;
- }
- public Inventor() {}
- public String getName() { return name;}
- public void setName(String name) { this.name = name;}
- public String getNationality() { return nationality;}
- public void setNationality(String nationality) { this.nationality = nationality; }
- public Date getBirthdate() { return birthdate;}
- public void setBirthdate(Date birthdate) { this.birthdate = birthdate;}
- public PlaceOfBirth getPlaceOfBirth() { return placeOfBirth;}
- public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) { this.placeOfBirth = placeOfBirth;}
- public void setInventions(String[] inventions) { this.inventions = inventions; }
- public String[] getInventions() { return inventions;}
- }
PlaceOfBirth.java
- package org.spring.samples.spel.inventor;
- public class PlaceOfBirth {
- private String city;
- private String country;
- public PlaceOfBirth(String city) {this.city=city;}
- public PlaceOfBirth(String city, String country){
- this(city);
- this.country = country;
- }
- public String getCity() {return city;}
- public void setCity(String s) {this.city = s;}
- public String getCountry() {return country;}
- public void setCountry(String country) {this.country = country;}
- }
Society.java
- package org.spring.samples.spel.inventor;
- import java.util.*;
- public class Society {
- private String name;
- public static String Advisors = "advisors";
- public static String President = "president";
- private List<Inventor> members = new ArrayList<Inventor>();
- private Map officers = new HashMap();
- public List getMembers() {return members;}
- public Map getOfficers() {return officers;}
- public String getName() {return name;}
- public void setName(String name) {this.name = name;}
- public boolean isMember(String name){
- boolean found = false;
- for (Inventor inventor : members) {
- if (inventor.getName().equals(name)){
- found = true;
- break;
- }
- }
- return found;
- }
- }
三)EvaluationContext接口
当在使用表达式的过程中有遇到类型转换时,我们需要EvaluationContext接口加以辅助:
- class Simple {
- public List<Boolean> booleanList = new ArrayList<Boolean>();
- }
- Simple simple = new Simple();
- simple.booleanList.add(true);
- EvaluationContext simpleContext = new StandardEvaluationContext(simple);
- // false is passed in here as a string. SpEL and the conversion service will
- // correctly recognize that it needs to be a Boolean and convert it
- parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");
- // b will be false
- Boolean b = simple.booleanList.get(0);
注:EvaluationContext开销较大,所以多用于数据变化较少的情况。如果数据变化频繁,我们可以考虑直接引用对象(比如上例中的simple)以减小开销。
四)SpEL
下面正式开始介绍SpEL。
1'Literal expressions 基本类的表达式
- 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();
2' Properties, Arrays, Lists, Maps, Indexers 属性、集合的表示
Properties:
- // evals to 1856
- int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
- String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
arrays and lists:
- 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:
- // Officer's Dictionary
- 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");
3' construction
lists:
- // 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);
arrays:
- 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);
4' Methods
- // 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);
5' Operators
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);
- // 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);
注意关键字:instanceof、matches
Logical operators:
- // evaluates to false
- boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
- // evaluates to true
- String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
- boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
- // -- OR -//
- evaluates to true
- boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
- // evaluates to true
- String expression = "isMember('Nikola Tesla') or isMember('Albert Einstien')";
- boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
- // -- NOT -//
- evaluates to false
- boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
- // -- AND and NOT -String
- expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
- boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Mathematical operators:
- // Addition
- int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
- String testString =
- parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
- // Subtraction
- int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
- double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
- // Multiplication
- int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
- double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
- // Division
- int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
- double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
- // Modulus
- int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
- int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
- // Operator precedence
- int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
6' Assignment
- 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);
7' Types
- 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);
注: 在java.lang中的类不需要全路径。
8' Constructors
- 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);
9' Variables
- 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"
注: Spring还有两个预留的variables: #this #root #this表示集合中的当前元素,#root表示根对象,即载入context的对象。
10' Functions
Function的使用略微复杂。首先是调用StandardEvaluationContext的方法
- 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);
11'Bean references
- 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);
没啥好多说的,知道用@就可以了~~
12'Ternary Operator
- String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
结果是: falseExp
13'The Elvis Operator
这是一种对三重操作符的简化写法。比如用三重操作符我们可以这样:
- String name = "Elvis Presley";
- String displayName = name != null ? name : "Unknown";
等效的用Elvis操作符实现的话就是这样:
- ExpressionParser parser = new SpelExpressionParser();
- String name = parser.parseExpression("null?:'Unknown'").getValue(String.class);
- System.out.println(name); // 'Unknown'
省略了name的显示表达!
14'Safe Navigation operator
用?.取代. 当代码会抛出NullPointerException时,采用?.的操作仅仅会简单的返回一个null。
- ExpressionParser parser = new SpelExpressionParser();
- Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
- tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
- StandardEvaluationContext context = new StandardEvaluationContext(tesla);
- String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
- System.out.println(city); // Smiljan
- tesla.setPlaceOfBirth(null);
- city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
- System.out.println(city); // null - does not throw NullPointerException!!!
15' Collection Selection
如果你要在集合内挑选符合某些条件的元素,你可以采用这样格式的表达式?[selectionExpression]
- List<Inventor> list = (List<Inventor>)
- parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext);
- Map newMap = parser.parseExpression("map.?[value<27]").getValue();
而如果你只需要获得第一个满足条件的元素,你可以用:^[...]
而如果你只需要获得最后一个满足条件的元素,你可以用:$[...]
16' Collection Projection
那么我需要便利某些元素以组成新的集合,我们可以采用这样的表达式![projectionExpression]:
- // returns [ 'Smiljan', 'Idvor' ]
- List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
17' Expression templating
template的作用便是在表达式内再嵌套表达式。比如:
- String randomPhrase =
- parser.parseExpression("random number is #{T(java.lang.Math).random()}",new TemplateParserContext()).getValue(String.class);
- // evaluates to "random number is 0.7038186818312008"
而要实现内部嵌套的T(java.lang.Math).random()表达式。我们需要实现接口ParserContext:
- public class TemplateParserContext implements ParserContext {
- public String getExpressionPrefix() {
- return "#{";
- }
- public String getExpressionSuffix() {
- return "}";
- }
- public boolean isTemplate() {
- return true;
- }
- }
五)SpEL在配置中的使用
同样的语法,SpEL还能在配置文件和annotation中使用!!强大吧 ^.^ 它们的格式是#{ <expression string> }
在配置文件中:
- <bean id="numberGuess" class="org.spring.samples.NumberGuess">
- <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
- <!-- other properties -->
- </bean>
- <bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
- <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
- <!-- other properties -->
- </bean>
- <bean id="numberGuess" class="org.spring.samples.NumberGuess">
- <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
- <!-- other properties -->
- </bean>
- <bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
- <property name="initialShapeSeed" value="#{numberGuess.randomNumber }"/>
- <!-- other properties -->
- </bean>
在annotation中使用:
- public static class FieldValueTestBean{
- @Value("#{ systemProperties['user.region'] }")
- private String defaultLocale;