SpEL (Spring Expression Language) 是 Spring 框架中的一种强大的表达式语言,用于在运行时查询和操作对象图。它可以在 XML 或注解配置中使用,也可以通过解析器 API 直接使用。
SpEL 支持对字符串的模板化、算术运算、关系运算、逻辑运算、正则表达式匹配以及集合操作等。
SpEL旨在简化Spring框架中的配置和编程,提供一种统一且强大的表达式求值机制。
可以与Spring框架的其他部分(如依赖注入、AOP、事件处理等)无缝集成。
基本语法
SpEL提供了丰富的语法来支持各种表达式的编写。
字面量表达式
字面量表达式是指那些直接表示值的表达式。
"Hello, World!" // 字符串
86 // 整数
3.14 // 浮点数
true // 布尔值
属性访问
属性访问表达式用于访问对象的属性。
可以引用上下文中的变量或属性。
#{user.name} // 引用名为 user 的对象的 name 属性
方法调用
方法调用表达式用于调用对象的方法。
- 无参数方法调用:spel这将调用名为
#someBean.someMethod()
someBean
的Bean的someMethod
方法。 - 带参数方法调用:spel这将调用
#someBean.someMethod('arg1', 42)
someBean
的someMethod
方法,并传递字符串'arg1'
和数字42
作为参数。
构造器调用
可以直接创建新实例。
new java.util.ArrayList<>() // 创建一个 ArrayList 实例
算术运算
算术运算表达式用于执行数学运算,如 +
, -
, *
, /
, %
等。
1 + 2
5 - 3
4 * 2
10 / 2
关系运算符
关系运算符用于比较两个值,如 <
, <=
, >
, >=
, ==
, !=
等。
- 等于:spel结果为true。
'Hello' == 'Hello'
- 不等于:spel结果为true。
'Hello' != 'World'
- 大于:spel结果为true。
5 > 3
逻辑运算符
逻辑运算符用于组合多个布尔表达式,如 and
, or
, !
等。
true && true
c and d // 逻辑与
true || false // 逻辑或
三元运算符
三元运算符用于条件判断。
- 条件表达式:spel结果为
'Condition is ' + (true ? 'true' : 'false')
'Condition is true'
。
类型操作
可以使用 T()
获取类的静态部分,并进行类型转换。
// 获取 Math 类的 random 静态方法
T(java.lang.Math).random()
// 将字符串转换为 Integer
T(Integer).valueOf('42')
集合操作
- 选择:从集合中选择满足特定条件的元素。
- 投影:对集合中的每个元素应用一个表达式。
- 索引和切片:获取列表或数组中的单个元素或子集。
查看代码
// 假设 users 是一个包含 Person 对象的列表
users.?[age > 18] // 选择所有年龄大于 18 的用户
// 将所有用户的姓名转为大写
users.![name.toUpperCase()]
// 获取第一个用户
users[0]
// 获取前两个用户
users.^[0:2]
Bean 引用
Bean引用直接在表达式中引用Spring容器中定义的Bean。
使用 @
符号进行Bean引用
在SpEL表达式中,可以使用 @
符号来引用一个Bean。通常需要指定Bean的ID或名称。
@exampleBean
访问Bean的属性和方法
一旦引用了一个Bean,可以访问它的属性和方法,就像在Java代码中一样。
@exampleBean.value
@exampleBean.calculate()
内置函数
SpEL提供了一些内置函数,这些函数可以直接在表达式中使用,以执行常见的操作。
1. size()
- 用途:获取集合、数组或字符串的大小。
- 示例:java
// 假设有一个 List 对象 users #{users.size()} // 获取 users 列表的大小
2. isEmpty()
- 用途:检查集合、数组或字符串是否为空。
- 示例:java
// 检查 users 列表是否为空 #{users.isEmpty()}
3. contains()
- 用途:检查集合、数组或字符串是否包含指定的元素或子串。
- 示例:java
// 检查 users 列表是否包含某个用户对象 #{users.contains(user)} // 检查字符串是否包含某个子串 #{'Hello, World!'.contains('World')}
4. index()
和 indexOf()
- 用途:返回集合、数组或字符串中某个元素或子串的第一个出现位置。
- 示例:java
// 返回 users 列表中 user 对象的位置 #{users.indexOf(user)} // 返回字符串中 'World' 的位置 #{'Hello, World!'.indexOf('World')}
5. matches()
- 用途:检查字符串是否匹配给定的正则表达式。
- 示例:java
// 检查字符串是否是有效的电子邮件地址 #{'example@example.com'.matches('.+@.+\\..+')}
6. T()
和 T(...).method(...)
用途:访问类的静态方法和常量。
示例:
java// 调用 Math 类的 random 方法 #{T(java.lang.Math).random()} // 访问 Integer 类的 MAX_VALUE 常量 #{T(Integer).MAX_VALUE}
7. #this
和 #root
在Spring Expression Language (SpEL)中,#this
和 #root
是特殊的变量,它们用于引用表达式求值过程中的上下文对象。
#this
用途:
#this
变量用于引用当前正在求值的对象。在表达式中,它可以用来访问当前对象的属性或调用其方法。示例:
假设有一个
Person
类,其中包含name
和age
属性,以及一个方法getFullName()
。如果在SpEL表达式中,当前上下文是
Person
对象,则可以使用#this
来引用它:spel#{#this.name} // 返回当前 Person 对象的 name 属性 #{#this.getFullName()} // 调用当前 Person 对象的 getFullName() 方法
#root
用途:
#root
变量用于引用表达式的根上下文对象。在大多数情况下,#root
和#this
引用的是同一个对象,但在嵌套表达式中,#this
可能会改变,而#root
始终引用最初的表达式上下文。示例:
继续使用上面的
Person
类,假设我们有一个更复杂的表达式,其中包含嵌套表达式:spel#{#root.name} // 返回根 Person 对象的 name 属性 #{#root.getFullName()} // 调用根 Person 对象的 getFullName() 方法
在以下嵌套表达式的例子中,
#this
和#root
的区别变得明显:spel#{#root.address.city} // 假设 Person 有一个 Address 属性,这里返回根 Person 对象的 address 的 city 属性 #{#root.address['city']} // 同上,使用索引访问器 #{#this} // 在这个上下文中,如果是在 Address 对象的表达式中,则返回当前 Address 对象
在最后一个例子中,如果表达式是在
Address
对象的上下文中求值的,那么#this
将引用Address
对象,而#root
仍然引用最外层的Person
对象。#{#this.address.city} // 如果当前上下文是 Person 对象,则引用 Person 的 address 属性的 city 属性
在这个表达式中,如果
address
是一个嵌套对象,那么在street
属性上的操作中,#this
将引用address
对象。