Spring-SpEL
参考文献
Spring Expression Language (SpEL)
支持的功能
功能 | 说明 |
---|---|
Literal expressions |
字面量表达式 |
Boolean and relational operators |
布尔和关系运算符 |
Regular expressions |
正则表达式 |
Class expressions |
类表达式 |
Accessing properties, arrays, lists, and maps |
访问属性、数组、列表和映射 |
Method invocation |
方法调用 |
Assignment |
赋值 |
Calling constructors |
调用构造函数 |
Bean references |
Bean引用 |
Array construction |
数组构建 |
Inline lists |
内联列表 |
Inline maps |
内嵌Map |
Ternary operator |
三元运算符 |
Variables |
变量 |
User-defined functions added to the context |
添加到上下文的用户定义函数 |
reflective invocation of Method |
方法的反射调用 |
various cases of MethodHandle |
MethodHandle的各种情况 |
Collection projection |
集合投影 |
Collection selection |
集合选择 |
Templated expressions |
模板化表达式 |
Literal expressions
- SpEL 支持以下类型的字面量表达式.
strings
numeric values: integer (int or long), hexadecimal (int or long), real (float or double)
boolean values: true or false
null
- 字符串可以用单引号 (
'
) 或双引号 ("
) 分隔.要在用单引号括起来的字符串文字中包含单引号,请使用两个相邻的单引号字符.同样,要在用双引号括起来的字符串文字中包含双引号,请使用两个相邻的双引号字符. - 数字支持使用负号、指数表示法和小数点.默认情况下,使用
Double.parseDouble()
解析实数.
Accessing properties, arrays, lists, and maps
-
数组和列表的内容是通过使用方括号表示法获得的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19ExpressionParser 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); -
映射的内容是通过指定括号内的文字键值来获取的
1
2
3
4
5
6
7
8
9
10
11
12// 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");
Inline Lists
- 可以使用
{}
表示法直接在表达式中表达列表
1 | // evaluates to a Java list containing the four numbers |
Inline Maps
- 可以使用
{key:value}
表示法直接在表达式中表达映射
Operators
Relational Operators
关系运算符Logical Operators
逻辑运算符Mathematical Operators
数学运算符The Assignment Operator
赋值运算符
Relational Operators
-
使用标准运算符表示法支持关系运算符(等于、不等于、小于、小于或等于、大于和大于或等于).这些运算符适用于
Number
类型以及实现Comparable
的类型1
2
3
4
5
6
7
8
9
10
11// 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);
// uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);- 与
null
的大于和小于比较遵循一个简单的规则:null
被视为无(即不为零).因此,任何其他值始终大于null
(X > null
始终true
),并且任何其他值都不会小于任何值(X < null
).
- 与
-
除了标准关系运算符之外,SpEL 还支持
instanceof
和基于正则表达式的matches
运算符1
2
3
4
5
6
7
8
9
10
11// 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
(<
)lt
(<
)gt
(>
)gt
(>
)le
(<=
)le
(<=
)ge
(>=
)ge
(>=
)eq
(==
)eq
(==
)ne
(!=
)ne
(!=
)div
(/
)div
(/
)mod
(%
)mod
(%
)not
(!
).not
(!
)
Logical Operators
- SpEL 支持以下逻辑运算符:
and
(&&
)and
(&&
)or
(||
)or
(||
)not
(!
)not
(!
)
1 | // -- AND -- |
Mathematical Operators
-
可以对数字和字符串使用加法运算符 (
+
).只能对数字使用减法 (-
)、乘法 (*
) 和除法 (/
) 运算符.还可以对数字使用模数 (%
) 和指数幂 (^
) 运算符 -
强制执行标准运算符优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 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
The Assignment Operator
-
要设置属性,请使用赋值运算符 (
=
).这通常在对setValue
的调用中完成,但也可以在对getValue
的调用中完成.1
2
3
4
5
6
7
8Inventor 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);
Types
- 可以使用特殊的
T
运算符来指定java.lang.Class
(类型)的实例.静态方法也可以使用此运算符来调用.StandardEvaluationContext
使用TypeLocator
查找类型,StandardTypeLocator
(可以替换)是在理解java.lang
的情况下构建的包裹.这意味着T()
对java.lang
包中类型的引用不需要完全限定,但所有其他类型引用必须完全限定.
1 | Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); |
Constructors
-
可以使用
new
运算符调用构造函数.应该对除java.lang
包中的类型(Integer
、Float
、String
等等).1
2
3
4
5
6
7
8Inventor einstein = p.parseExpression(
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
.getValue(Inventor.class);
// create new Inventor instance within the add() method of List
p.parseExpression(
"Members.add(new org.spring.samples.spel.inventor.Inventor(
'Albert Einstein', 'German'))").getValue(societyContext);
Variables
- 可以使用
#variableName
语法引用表达式中的变量.变量是通过在EvaluationContext
实现上使用setVariable
方法来设置的 - 有效的变量名称必须由以下一个或多个受支持的字符组成.
- letters:
A
toZ
anda
toz
- digits:
0
to9
- underscore:
_
- dollar sign:
$
- letters:
Bean References
-
如果评估上下文已配置了 Bean 解析器,则可以使用
@
符号从表达式中查找 Bean.1
2
3
4
5
6ExpressionParser 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 名称前加上
&
符号作为前缀.1
2
3
4
5
6ExpressionParser 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);
Safe Navigation Operator
-
安全导航运算符用于避免
NullPointerException
,来自 Groovy 语言.通常,当拥有对对象的引用时,可能需要在访问该对象的方法或属性之前验证它是否不为null
.为了避免这种情况,安全导航运算符返回null
而不是抛出异常.1
2
3
4
5
6
7
8
9
10
11
12ExpressionParser 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); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
Collection Selection
-
选择使用
.?[selectionExpression]
语法.它过滤集合并返回包含原始元素子集的新集合- 相当于
Java 8 Stream#filter
操作
1
2List<Inventor> list = (List<Inventor>) parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext); - 相当于
-
数组和任何实现
java.lang.Iterable
或java.util.Map
的对象都支持选择.对于列表或数组,选择标准是针对每个单独的元素进行评估的.针对映射,根据每个映射条目(Java
类型Map.Entry
的对象)评估选择标准.每个地图条目都有其key
和value
可作为属性访问以在选择中使用. -
除了返回所有选定的元素之外,还可以仅检索第一个或最后一个元素.要获取与选择匹配的第一个元素,语法为
.^[selectionExpression]
.要获取最后的匹配选择,语法为.$[selectionExpression]
.
Collection Projection
-
投影让集合驱动子表达式的计算,结果是一个新集合.投影的语法是
.![projectionExpression]
.- 相当于
Java 8 Stream#map
操作
1
2// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]"); - 相当于
-
数组和任何实现
java.lang.Iterable
或java.util.Map
的对象都支持投影.当使用映射来驱动投影时,将针对映射中的每个条目(表示为 JavaMap.Entry
)计算投影表达式.跨地图投影的结果是一个列表,其中包含针对每个地图条目的投影表达式的评估.
使用方式
注解@Value
中
1 | //@Value能修饰成员变量和方法形参 |
XML
配置
-
字面量赋值必须要和对应的属性类型兼容,否则会报异常。
1
2
3
4<bean id="xxx" class="cn.holelin.test.bean">
<!-- 同 ,#{}内是表达式的值,可放在property或constructor-arg内 -->
<property name="name" value="#{表达式}">
</bean> -
引用Bean、属性和方法(必须是public修饰的)
1
2
3
4
5<property name="car" value="#{car}" />
<!-- 引用其他对象的属性 -->
<property name="carName" value="#{car.name}" />
<!-- 引用其他对象的方法 -->
<property name="carPrint" value="#{car.print()}" />
代码块中使用Expression
1 | ExpressionParser parser = new SpelExpressionParser(); |
方法调用
1 | ExpressionParser parser = new SpelExpressionParser(); |
属性调用
1 | ExpressionParser parser = new SpelExpressionParser(); |
- SpEL 还通过使用标准点表示法(例如
prop1.prop2.prop3
)以及相应的属性值设置来支持嵌套属性.也可以访问公共字段.
组件
EvaluationContext
EvaluationContext
接口用于计算表达式以解析属性、方法或字段并帮助执行类型转换. Spring提供了两种实现方式.SimpleEvaluationContext
:公开 SpEL 语言基本功能和配置选项的子集,适用于不需要 SpEL 语言语法完整范围且应受到有意义限制的表达式类别.示例包括但不限于数据绑定表达式和基于属性的过滤器.StandardEvaluationContext
:公开全套 SpEL 语言功能和配置选项.可以使用它来指定默认根对象并配置每个可用的评估相关策略.
SimpleEvaluationContext
旨在仅支持 SpEL 语言语法的子集.它不包括 Java 类型引用、构造函数和 bean 引用.它还要求显式选择表达式中属性和方法的支持级别.默认情况下,create()
静态工厂方法仅启用对属性的读取访问.还可以获取构建器来配置所需的确切支持级别,针对以下一项或某项组合:Custom PropertyAccessor only (no reflection)
Data binding properties for read-only access
Data binding properties for read and write
Type Conversion
-
默认情况下,SpEL 使用 Spring 核心中提供的转换服务 (
org.springframework.core.convert.ConversionService
).此转换服务附带许多用于常见转换的内置转换器,但也完全可扩展,以便可以在类型之间添加自定义转换.此外,它还具有泛型意识.这意味着,当在表达式中使用泛型类型时,SpEL 会尝试进行转换以维护它遇到的任何对象的类型正确性.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Simple {
public List<Boolean> booleanList = new ArrayList<>();
}
Simple simple = new Simple();
simple.booleanList.add(true);
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");
// b is false
Boolean b = simple.booleanList.get(0);
Parser Configuration
-
可以使用解析器配置对象 (
org.springframework.expression.spel.SpelParserConfiguration
) 来配置 SpEL 表达式解析器.配置对象控制某些表达式组件的行为. -
例如,如果对数组或集合进行索引,并且指定索引处的元素是
null
,SpEL 可以自动创建该元素.当使用由属性引用链组成的表达式时,这非常有用.如果对数组或列表进行索引并指定超出数组或列表当前大小末尾的索引,SpEL 可以自动增长数组或列表以容纳该索引.为了在指定索引处添加元素,SpEL 将在设置指定值之前尝试使用元素类型的默认构造函数创建元素.如果元素类型没有默认构造函数,null
将被添加到数组或列表中.如果没有知道如何设置值的内置或自定义转换器,null
将保留在数组或列表中的指定索引处.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Demo {
public List<String> list;
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
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 will now be a real collection of 4 entries
// Each entry is a new empty String
SpEL原理及接口
- 表达式: 表达式是表达语言的核心.用来表示"干什么"
- 解析器: 用不将字符串表达式解析为表达式对象.用来表示"谁来干"
- 上下文: 表达式对象执行的环境,该环境可能是定以变量,定义自定义函数,提供类型转换.用来表示"在哪干"
- 根对象及活动上下文对象: 根对象是默认的活动上下文对象,活动上下文对象表示当前表达式操作的对象.用来表示"对谁干"