此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
评估
本节介绍 SPEL 接口及其表达式语言的编程使用。 完整的语言参考可以在 语言参考 中找到。
以下代码演示了如何使用 SPEL API 评估文本字符串
表达Hello World
.
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); (1)
String message = (String) exp.getValue();
1 | message 变量的值为"Hello World" . |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'") (1)
val message = exp.value as String
1 | message 变量的值为"Hello World" . |
您最有可能使用的 SPEL 类和接口位于org.springframework.expression
package 及其子软件包,例如spel.support
.
这ExpressionParser
interface 负责解析表达式字符串。在
前面的示例,表达式 String 是一个字符串文本,由周围的
单引号。这Expression
interface 负责评估
定义的表达式字符串。调用parser.parseExpression(…)
和exp.getValue(…)
是ParseException
和EvaluationException
分别。
SPEL 支持广泛的功能,例如调用方法、访问属性、 并调用构造函数。
在以下方法调用示例中,我们将concat
method 对字符串
字面Hello World
.
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); (1)
String message = (String) exp.getValue();
1 | 的值message 现在是"Hello World!" . |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'.concat('!')") (1)
val message = exp.value as String
1 | 的值message 现在是"Hello World!" . |
以下示例演示如何访问Bytes
JavaBean 属性的
string literal (字符串文本)、Hello World
.
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes"); (1)
byte[] bytes = (byte[]) exp.getValue();
1 | 此行将 Literals 转换为字节数组。 |
val parser = SpelExpressionParser()
// invokes 'getBytes()'
val exp = parser.parseExpression("'Hello World'.bytes") (1)
val bytes = exp.value as ByteArray
1 | 此行将 Literals 转换为字节数组。 |
SPEL 还通过使用标准点表示法(例如prop1.prop2.prop3
) 以及相应的属性值设置。
还可以访问 Public 字段。
以下示例演示如何使用点表示法获取字符串文本的长度。
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); (1)
int length = (Integer) exp.getValue();
1 | 'Hello World'.bytes.length 给出文本的长度。 |
val parser = SpelExpressionParser()
// invokes 'getBytes().length'
val exp = parser.parseExpression("'Hello World'.bytes.length") (1)
val length = exp.value as Int
1 | 'Hello World'.bytes.length 给出文本的长度。 |
可以调用 String 的构造函数,而不是使用字符串文本,如下所示 示例显示。
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); (1)
String message = exp.getValue(String.class);
1 | 构造一个新的String 并将其转换为大写。 |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("new String('hello world').toUpperCase()") (1)
val message = exp.getValue(String::class.java)
1 | 构造一个新的String 并将其转换为大写。 |
请注意 generic 方法的使用:public <T> T getValue(Class<T> desiredResultType)
.
使用此方法无需将表达式的值强制转换为所需的值
result 类型。一EvaluationException
如果值无法强制转换为
类型T
或使用已注册的类型转换器进行转换。
SPEL 更常见的用法是提供一个经过评估的表达式字符串
针对特定对象实例 (称为根对象) 。以下示例显示了
如何检索name
属性从Inventor
类和作方法
引用name
属性。
-
Java
-
Kotlin
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
// Create and set a calendar
val c = GregorianCalendar()
c.set(1856, 7, 9)
// The constructor arguments are name, birthday, and nationality.
val tesla = Inventor("Nikola Tesla", c.time, "Serbian")
val parser = SpelExpressionParser()
var exp = parser.parseExpression("name") // Parse name as an expression
val name = exp.getValue(tesla) as String
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'")
val result = exp.getValue(tesla, Boolean::class.java)
// result == true
理解EvaluationContext
这EvaluationContext
API 用于计算表达式以解析属性,
方法或字段,并帮助执行类型转换。Spring 提供两个
实现。
SimpleEvaluationContext
-
公开基本 SPEL 语言功能和配置选项的子集,用于 不需要 SpEL 语言的完整范围的表达式的类别 语法,并且应该进行有意义的限制。示例包括但不限于 数据绑定表达式和基于属性的筛选器。
StandardEvaluationContext
-
公开了全套 SPEL 语言功能和配置选项。您可以使用 它指定一个默认的根对象并配置每个可用的与评估相关的 策略。
SimpleEvaluationContext
旨在仅支持 SPEL 语言的子集
语法。例如,它不包括 Java 类型引用、构造函数和 Bean 引用。
它还要求您显式选择对属性和方法的支持级别
in 表达式。创建SimpleEvaluationContext
您需要选择
支持在 SPEL 表达式中进行数据绑定:
-
用于只读访问的数据绑定
-
用于读取和写入访问的数据绑定
-
自定义
PropertyAccessor
(通常不是基于反射的),可能与 一个DataBindingPropertyAccessor
顺手SimpleEvaluationContext.forReadOnlyDataBinding()
启用只读访问
到 properties 通过DataBindingPropertyAccessor
.同样地SimpleEvaluationContext.forReadWriteDataBinding()
启用对
性能。或者,通过SimpleEvaluationContext.forPropertyAccessors(…)
、可能禁用分配以及
(可选)通过构建器激活 Method Resolution 和/或 Type Converter。
类型转换
默认情况下,SPEL 使用 Spring 核心中提供的转换服务
(org.springframework.core.convert.ConversionService
).此转换服务随之而来
具有许多用于常见转换的内置转换器,但也具有完全可扩展性,因此
,您可以在类型之间添加自定义转换。此外,它是泛型感知的。
这意味着,当您在表达式中使用泛型类型时, SPEL 会尝试
conversions 来保持它遇到的任何对象的类型正确性。
这在实践中意味着什么?假设赋值,使用setValue()
正在使用
要设置List
财产。属性的类型实际上是List<Boolean>
.斯佩尔
识别出需要将 List 的元素转换为Boolean
以前
被放置在其中。以下示例说明如何执行此作。
-
Java
-
Kotlin
class 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);
class Simple {
var booleanList: MutableList<Boolean> = ArrayList()
}
val simple = Simple()
simple.booleanList.add(true)
val 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
val b = simple.booleanList[0]
解析器配置
可以使用解析器配置来配置 SpEL 表达式解析器
对象 (org.springframework.expression.spel.SpelParserConfiguration
).配置
object 控制某些表达式组件的行为。例如,如果你
index 添加到集合中,并且指定索引处的元素为null
,SpEL 可以
自动创建元素。当使用由
属性引用链。同样,如果您索引到集合中并指定
索引大于集合的当前大小,则 SpEL 可以自动
增大集合以容纳该索引。要在
指定的索引,则 SPEL 将尝试使用元素类型的默认值创建元素
constructor 在设置指定值之前。如果元素类型没有
default 构造函数、null
将添加到集合中。如果没有内置的
converter 或知道如何设置值的自定义转换器,null
将保留在
集合。以下示例演示了如何
自动增长List
.
-
Java
-
Kotlin
class 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
class Demo {
var list: List<String>? = null
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
val config = SpelParserConfiguration(true, true)
val parser = SpelExpressionParser(config)
val expression = parser.parseExpression("list[3]")
val demo = Demo()
val o = expression.getValue(demo)
// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
默认情况下,SPEL 表达式不能包含超过 10,000 个字符;但是,maxExpressionLength
是可配置的。如果您创建一个SpelExpressionParser
您可以通过编程方式指定自定义maxExpressionLength
创建SpelParserConfiguration
,提供给SpelExpressionParser
.如果您愿意
将maxExpressionLength
用于解析 SpEL 表达式中的ApplicationContext
— 例如,在 XML bean 定义中,@Value
等 — 您可以
设置一个名为spring.context.expression.maxLength
设置为应用程序所需的最大表达式长度(请参阅支持的 Spring 属性)。
SPEL 编译
Spring 为 SPEL 表达式提供了一个基本的编译器。表达式通常是 interpreted,这在求值期间提供了很大的动态灵活性,但不会 提供最佳性能。对于偶尔的表达式使用,这很好,但是,当 被其他组件(如 Spring Integration)使用,性能可能非常重要, 而且没有真正需要这种活力。
SPEL 编译器旨在满足这一需求。在评估期间,编译器 生成一个 Java 类,该类在运行时体现表达式行为,并使用该类 类来实现更快的表达式计算。由于周围缺乏打字 expressions,编译器会使用在解释的评估期间收集的信息 的表达式。例如,它不知道类型 的属性引用,但在第一次解释的 evaluation 时,它会找出它是什么。当然,基于这样的派生 如果各种表达式元素的类型 随时间变化。因此,编译最适合于其 type information 不会在重复计算时发生变化。
请考虑以下基本表达式。
someArray[0].someProperty.someOtherProperty < 0.1
由于前面的表达式涉及数组访问、某些属性取消引用和 numeric 运算时,性能提升可能非常明显。在微型 基准测试运行 50,000 次迭代,使用解释器评估需要 75 毫秒,并且 使用表达式的编译版本只需 3ms。
编译器配置
默认情况下,编译器未打开,但您可以通过以下两种方式之一打开它 不同的方式。您可以使用解析器配置过程来打开它 (已讨论 earlier),或者在 SpEL 用法嵌入到另一个 SpEL 用法中时使用 Spring 属性 元件。本节讨论这两个选项。
编译器可以在以下三种模式之一下运行,这些模式在org.springframework.expression.spel.SpelCompilerMode
enum 中。模式如下。
OFF
-
编译器已关闭,所有表达式都将在解释模式下计算。这是默认模式。
IMMEDIATE
-
在 immediate 模式下,表达式会尽快编译,通常在 首先解释评估。如果编译表达式的计算失败(对于 示例,由于类型更改,如前所述),表达式的调用方 evaluation 收到异常。如果各种表达式元素的类型发生变化 随着时间的推移,请考虑切换到
MIXED
模式或关闭编译器。 MIXED
-
在混合模式下,表达式计算会随着时间的推移在已解释和已编译之间以静默方式切换。在成功运行一定数量的解释运行后,表达式 被编译。如果编译表达式的计算失败(例如,由于 type changing),该失败将在内部捕获,并且系统将切换回来 设置为给定表达式的 Interpreted 模式。基本上,调用方 接收
IMMEDIATE
mode 则在内部处理。一段时间后, compiler 可能会生成另一个编译形式并切换到它。这个切换周期 在 Understood 和 Compiled 模式之间将继续运行,直到系统确定它 继续尝试没有意义 — 例如,当某个失败阈值 已到达 - 此时系统将永久切换到 Interpreted mode 的表达式。
IMMEDIATE
模式存在,因为MIXED
mode 可能会导致表达式出现问题
有副作用。如果编译的表达式在部分成功后崩溃,则
可能已经做了一些影响系统状态的事情。如果此
已发生,调用方可能不希望它在解释模式下静默重新运行。
因为表达式的一部分可能会运行两次。
选择模式后,使用SpelParserConfiguration
配置解析器。这
以下示例说明如何执行此作。
-
Java
-
Kotlin
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader());
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression expr = parser.parseExpression("payload");
MyMessage message = new MyMessage();
Object payload = expr.getValue(message);
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader)
val parser = SpelExpressionParser(config)
val expr = parser.parseExpression("payload")
val message = MyMessage()
val payload = expr.getValue(message)
指定编译器模式时,还可以指定ClassLoader
(通过null
是允许的)。编译后的表达式在子级ClassLoader
在任何
那是供应的。请务必确保,如果ClassLoader
,它可以
查看表达式计算过程中涉及的所有类型。如果未指定ClassLoader
,则默认为ClassLoader
(通常为上下文ClassLoader
为
在表达式计算期间运行的线程)。
配置编译器的第二种方法是在 SpEL 嵌入到某些
other 组件,并且可能无法通过配置
对象。在这种情况下,可以将spring.expression.compiler.mode
属性(或通过 JVM 系统属性的SpringProperties
机制)复制到SpelCompilerMode
枚举值 (off
,immediate
或mixed
).