本文结构文章参考来源:Spring官方文档 之 4. Spring expression Language (SpEL)
- 1. evaluation
- 2. Bean定义中的表达式
- 3. 语言引用_Language Reference
- 3.1 文字表达式
- 3.2 属性、数组、列表、Map映射和索引器
- 3.3 内联列表
- 3.4 内联Map映射
- 3.5 数组构建
- 3.6 方法
- 3.7 运算符
- 3.7.1 关系运算符
- 3.7.2 逻辑运算符
- 3.7.3 数学运算符
- 3.7.4 赋值运算符
- 3.8 三元 *** 作符(if - then - else)
- 3.9 Elvis *** 作符
- 3.10 集合筛选
- 3.11 表达式模板
SpEL虽然是Spring组合中表达式求值的基础,但它不直接绑定到Spring,可以独立使用。
为了实现自包含,本章中的许多例子使用了SpEL,就好像它是一种独立的表达语言。这需要创建一些引导基础设施类,比如解析器。大多数Spring用户不需要处理这个基础结构,相反,可以只编写用于计算的表达式字符串。这种典型用法的一个例子是将SpEL集成到创建XML或基于注释的bean定义中,如对定义bean定义的expression支持所示。
表达式语言支持以下功能:
- 文字表达方式
- 布尔运算符和关系运算符
- 正则表达式
- 类表达式
- 访问属性、数组、列表和映射
- 方法调用
- 关系运算符
- 赋值
- 调用构造函数
- Bean的引用
- 阵列结构
- 内联列表
- 内联映射
- 三元运算符
- 变量
- 用户定义函数
- 预测集合
- 选择集合
- 模板化表达式
一般来说,可能会被使用的SpEL类和接口位于org.springframework.expression包及其子包中,例如spell.support。
其中,expressionParser是一个负责解析表达式字符串的接口。expression接口负责求前面定义的表达式字符串的值。当调用parser.parseexpression和exp.getValue时,两个异常会被抛出:ParseException和evaluationException。
SpEL支持很多特性,比如调用方法、访问属性和调用构造函数。在下面的方法调用示例中,给出如何在字符串字面量上调用concat方法:
expressionParser parser = new SpelexpressionParser(); expression exp = parser.parseexpression("'Hello World'.concat('!')"); String message = (String) exp.getValue();
message的结果为:Hello World!
expressionParser parser = new SpelexpressionParser(); // invokes 'getBytes()' expression exp = parser.parseexpression("'Hello World'.bytes"); byte[] bytes = (byte[]) exp.getValue();
上述会将字符串Hello World转换为byte字节数组
SpEL甚至可以通过使用标准点表法(如prop1.prop2.prop3)和相应的属性值设置来支持嵌套属性。也可以访问公共字段。
下面的例子展示了如何使用点表示法来获取文本的长度:
expressionParser parser = new SpelexpressionParser(); // invokes 'getBytes().length' expression exp = parser.parseexpression("'Hello World'.bytes.length"); int length = (Integer) exp.getValue();2. Bean定义中的表达式
可以将SpEL表达式与基于xml或基于注释的配置元数据一起使用,以定义BeanDefinition实例。在这两种情况下,定义表达式的语法形式都是#{}。
(1)XML配置
(2)注解配置
比如要指定默认值,可以将@Value注释放在字段、方法或构造函数参数上。
设置字段的默认值的示例如下:
public class FieldValueTestBean { @Value("#{ systemProperties['user.region'] }") private String defaultLocale; public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } }
构造函数的参数设置:
public class MovieRecommender { private String defaultLocale; private CustomerPreferenceDao customerPreferenceDao; public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, @Value("#{systemProperties['user.country']}") String defaultLocale) { this.customerPreferenceDao = customerPreferenceDao; this.defaultLocale = defaultLocale; } // ... }3. 语言引用_Language Reference 3.1 文字表达式
字面表达式支持的类型有字符串、数值(int、real、hex)、布尔值和null。字符串由单引号分隔。要将单引号本身放入字符串中,请使用两个单引号字符。
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();
数字支持使用负号、指数符号和小数点。默认情况下,使用Double.parseDouble()解析实数。
3.2 属性、数组、列表、Map映射和索引器使用属性引用导航很容易。为此,使用句点来表示嵌套的属性值。
// evals to 1856 int year = (Integer) parser.parseexpression("birthdate.year + 1900").getValue(context); String city = (String) parser.parseexpression("placeOfBirth.city").getValue(context);
属性名的第一个字母允许不区分大小写。因此,上面例子中的表达式可以分别写成 Birthdate.Year + 1900 和PlaceOfBirth.City。此外,可以通过方法调用访问属性——例如,也可以使用getPlaceOfBirth().getCity()而不仅仅是placeOfBirth.city。
数组和列表的内容是用方括号表示的,如下例所示:
expressionParser parser = new SpelexpressionParser(); evaluationContext context = SimpleevaluationContext.forReadOnlyDataBinding().build(); // 数组 String invention = parser.parseexpression("inventions[3]").getValue( context, tesla, String.class); // 列表 String name = parser.parseexpression("members[0].name").getValue( context, ieee, String.class); // List and Array navigation String invention = parser.parseexpression("members[0].inventions[6]").getValue( context, ieee, String.class);
Map映射的内容是通过在括号中指定字面值键值获得的。在下面的例子中,因为officers 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.3 内联列表
可以使用{}符号直接在表达式中表示列表:
// 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);
{}本身意味着一个空列表。出于性能原因,如果列表本身完全由固定字面值组成,则创建一个常量列表来表示表达式(而不是在每次求值时构建一个新列表)。
3.4 内联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);
{:}本身表示一个空映射。出于性能原因,如果映射本身由固定文字或其他嵌套常量结构(列表或映射)组成,则创建一个常量映射来表示表达式(而不是在每次求值时构建一个新的映射)。map键的引用是可选的(除非键包含一个句点(.))。上面的示例不使用引号键。
3.5 数组构建可以使用熟悉的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);3.6 方法
可以使用典型的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);3.7 运算符
Spring表达式语言支持以下类型的 *** 作符:
- 关系运算符
- 逻辑运算符
- 数学运算符
- 赋值运算符
关系运算符示例:
// 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);
需要注意的是:针对大于null或小于null的比较,遵循一个简单的规则“null被视为零(即不是零)”。因此,任何其他值总是大于null (X > null总是为真),任何其他值都不小于nothing (X < null总是为假)。
如果更偏向于数值比较,那就避免基于数字的null比较,而倾向于对零进行比较(例如,X > 0或X < 0)。
除了标准的关系 *** 作符外,SpEL还支持instanceof和基于正则表达式的匹配 *** 作符:
// evaluates to false boolean falsevalue = parser.parseexpression( "'xyz' instanceof T(Integer)").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);
要注意基本类型,因为它们会立即被封装到包装器类型中。例如,1个instanceof T(int)的计算结果为false,而1个instanceof T(Integer)的计算结果为true。
还可以将每个符号 *** 作符指定为纯字母等价体。这避免了所使用的符号对所嵌入的表达式的文档类型具有特殊含义的问题(例如在XML文档中)。文本 *** 作符的“等价体”可以有(不区分大小写):
- lt (<)
- gt (>)
- le (<=)
- ge (>=)
- eq (==)
- ne (!=)
- div (/)
- mod (%)
- not (!).
SpEL支持以下逻辑运算符:
- and (&&)
- or (||)
- not (!)
// -- AND -- // 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 Einstein')"; 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);3.7.3 数学运算符
加法运算符(+)既可以作用于数字,也适用于字符串。但减法(-)、乘法(*)和除法(/)运算符,只能作用于数字。还可以对数字使用模数(%)和指数幂(^)运算符。强制执行标准 *** 作符优先级。下面的例子展示数学运算符的使用:
// 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); // -213.7.4 赋值运算符
要设置属性,请使用赋值 *** 作符(=)。这通常在setValue调用中完成,但也可以在getValue调用中完成。下面的清单显示了两种使用赋值 *** 作符的方法:
Inventor inventor = new Inventor(); evaluationContext context = SimpleevaluationContext.forReadWriteDataBinding().build(); parser.parseexpression("name").setValue(context, inventor, "Aleksandar Seovic"); // alternatively String aleks = parser.parseexpression( "name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);3.8 三元 *** 作符(if - then - else)
可以使用三元 *** 作符在表达式中执行if-then-else条件逻辑:
String falseString = parser.parseexpression( "false ? 'trueExp' : 'falseExp'").getValue(String.class);
一个复杂的例子:
parser.parseexpression("name").setValue(societyContext, "IEEE"); societyContext.setVariable("queryName", "Nikola Tesla"); expression = "isMember(#queryName)? #queryName + ' is a member of the ' " + "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"; String queryResultString = parser.parseexpression(expression) .getValue(societyContext, String.class); // queryResultString = "Nikola Tesla is a member of the IEEE Society"3.9 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(new Inventor(), String.class); System.out.println(name); // 'Unknown'
一个更为复杂点的例子:
expressionParser parser = new SpelexpressionParser(); evaluationContext context = SimpleevaluationContext.forReadOnlyDataBinding().build(); Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); String name = parser.parseexpression("name?:'Elvis Presley'").getValue(context, tesla, String.class); System.out.println(name); // Nikola Tesla tesla.setName(null); name = parser.parseexpression("name?:'Elvis Presley'").getValue(context, tesla, String.class); System.out.println(name); // Elvis Presley
甚至可以使用Elvis *** 作符在表达式中应用默认值。下面的例子展示了如何在@Value表达式中使用Elvis *** 作符:
@Value("#{systemProperties['pop3.port'] ?: 25}")3.10 集合筛选
集合筛选是一种强大的表达式语言特性,它允许通过从一个源集合的条目中进行选择来将其转换为另一个集合。它筛选集合并返回一个包含原始元素子集的新集合语法规则为:.?[selectionexpression]
Listlist = (List ) parser.parseexpression( "members.?[nationality == 'Serbian']").getValue(societyContext);
下面的表达式返回一个新的map,它包含原始map中元素的值小于27的元素:
Map newMap = parser.parseexpression("map.?[value<27]").getValue();3.11 表达式模板
表达式模板允许将文本与一个或多个求值块混合使用。每个计算块都由定义的前缀和后缀字符分隔。常见的选择是使用#{}作为分隔符,如下面的示例所示:
String randomPhrase = parser.parseexpression( "random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class); // evaluates to "random number is 0.7038186818312008"
在以上示例中,TemplateParserContext的使用会将由#{ }包起来的求值块先计算。默认情况下使用#{ }作为前后缀,但是其实也可以自定义,TemplateParserContext的代码如下:
package org.springframework.expression.common; import org.springframework.expression.ParserContext; public class TemplateParserContext implements ParserContext { private final String expressionPrefix; private final String expressionSuffix; //默认情况下,会去找#{ }包围的计算块 public TemplateParserContext() { this("#{", "}"); } //也可以自定义 public TemplateParserContext(String expressionPrefix, String expressionSuffix) { this.expressionPrefix = expressionPrefix; this.expressionSuffix = expressionSuffix; } public final boolean isTemplate() { return true; } public final String getexpressionPrefix() { return this.expressionPrefix; } public final String getexpressionSuffix() { return this.expressionSuffix; } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)