此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 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
spel.support
该接口负责解析表达式字符串。在
前面的示例,表达式 String 是一个字符串文本,由周围的
单引号。该接口负责评估
定义的表达式字符串。调用 和 和 时可以引发的两种类型的异常分别是 和 。ExpressionParser
Expression
parser.parseExpression(…)
exp.getValue(…)
ParseException
EvaluationException
SPEL 支持广泛的功能,例如调用方法、访问属性、 并调用构造函数。
在下面的方法调用示例中,我们对字符串
字面。concat
Hello World
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); (1)
String message = (String) exp.getValue();
1 | 的值为 now 。message "Hello World!" |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'.concat('!')") (1)
val message = exp.value as String
1 | 的值为 now 。message "Hello World!" |
以下示例演示如何访问
字符串文本、 .Bytes
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 还通过使用标准点表示法(例如 )以及相应的属性值设置来支持嵌套属性。
还可以访问 Public 字段。prop1.prop2.prop3
以下示例演示如何使用点表示法获取字符串文本的长度。
-
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 | 从文本构造一个 new 并将其转换为大写。String |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("new String('hello world').toUpperCase()") (1)
val message = exp.getValue(String::class.java)
1 | 从文本构造一个 new 并将其转换为大写。String |
请注意泛型方法的使用:.
使用此方法无需将表达式的值强制转换为所需的值
result 类型。如果值无法强制转换为
type 或 converted 使用已注册的类型转换器。public <T> T getValue(Class<T> desiredResultType)
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
在计算表达式以解析属性时使用 API,
方法或字段,并帮助执行类型转换。Spring 提供两个
实现。EvaluationContext
SimpleEvaluationContext
-
公开基本 SPEL 语言功能和配置选项的子集,用于 不需要 SpEL 语言的完整范围的表达式的类别 语法,并且应该进行有意义的限制。示例包括但不限于 数据绑定表达式和基于属性的筛选器。
StandardEvaluationContext
-
公开了全套 SPEL 语言功能和配置选项。您可以使用 它指定一个默认的根对象并配置每个可用的与评估相关的 策略。
SimpleEvaluationContext
旨在仅支持 SPEL 语言的子集
语法。例如,它不包括 Java 类型引用、构造函数和 Bean 引用。
它还要求您显式选择对属性和方法的支持级别
in 表达式。创建 时,您需要选择
支持在 SPEL 表达式中进行数据绑定:SimpleEvaluationContext
-
用于只读访问的数据绑定
-
用于读取和写入访问的数据绑定
-
自定义(通常不是基于反射的),可能与 一个
PropertyAccessor
DataBindingPropertyAccessor
方便地启用只读访问
到 Properties 。同样,启用对
性能。或者,通过 、 可能禁用赋值和
(可选)通过构建器激活 Method Resolution 和/或 Type Converter。SimpleEvaluationContext.forReadOnlyDataBinding()
DataBindingPropertyAccessor
SimpleEvaluationContext.forReadWriteDataBinding()
SimpleEvaluationContext.forPropertyAccessors(…)
类型转换
默认情况下,SPEL 使用 Spring 核心中提供的转换服务
().此转换服务随之而来
具有许多用于常见转换的内置转换器,但也具有完全可扩展性,因此
,您可以在类型之间添加自定义转换。此外,它是泛型感知的。
这意味着,当您在表达式中使用泛型类型时, SPEL 会尝试
conversions 来保持它遇到的任何对象的类型正确性。org.springframework.core.convert.ConversionService
这在实践中意味着什么?假设正在使用 的赋值 ,
以设置属性。属性的类型实际上是 。斯佩尔
识别出列表的元素需要转换为 before
被放置在其中。以下示例说明如何执行此操作。setValue()
List
List<Boolean>
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 表达式解析器
对象 () 。配置
object 控制某些表达式组件的行为。例如,如果你
index 放入集合中,并且指定索引处的元素为 ,则 SPEL 可以
自动创建元素。当使用由
属性引用链。同样,如果您索引到集合中并指定
索引大于集合的当前大小,则 SpEL 可以自动
增大集合以容纳该索引。要在
指定的索引,则 SPEL 将尝试使用元素类型的默认值创建元素
constructor 在设置指定值之前。如果元素类型没有
default 构造函数将添加到集合中。如果没有内置的
converter 或知道如何设置该值的自定义转换器将保留在
集合。以下示例演示了如何
自动增大 .org.springframework.expression.spel.SpelParserConfiguration
null
null
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 个字符;但是,这是可配置的。如果以编程方式创建,则可以在创建提供给 .如果您愿意
设置用于解析 SPEL 表达式的 — 例如,在 XML bean 定义中,等等 — 您可以
将 JVM 系统属性或 Spring 属性名称设置为应用程序所需的最大表达式长度(请参阅支持的 Spring 属性)。maxExpressionLength
SpelExpressionParser
maxExpressionLength
SpelParserConfiguration
SpelExpressionParser
maxExpressionLength
ApplicationContext
@Value
spring.context.expression.maxLength
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
-
OFF
(默认):编译器已关闭。 -
IMMEDIATE
:在即时模式下,表达式会尽快编译。这 通常在第一次解释的评估之后。如果编译的表达式失败 (通常是由于类型更改,如前所述),表达式的调用方 evaluation 收到异常。 -
MIXED
:在混合模式下,表达式在解释和 编译模式。经过一定数量的解释运行后,它们会切换到 compiled 表单,如果编译后的表单出现问题(例如类型更改,如 如前所述),表达式会自动切换回解释形式 再。稍后,它可能会生成另一个编译的表单并切换到它。 基本上,用户进入模式的异常会被处理 内部。IMMEDIATE
IMMEDIATE
mode 存在,因为 mode 可能会导致
有副作用。如果编译的表达式在部分成功后崩溃,则
可能已经做了一些影响系统状态的事情。如果此
已发生,调用方可能不希望它在解释模式下静默重新运行。
因为表达式的一部分可能会运行两次。MIXED
选择模式后,使用 配置解析器。这
以下示例说明如何执行此操作。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)
指定编译器模式时,还可以指定 (允许传递)。编译的表达式在任意
那是供应的。请务必确保,如果指定了 a,则它可以
查看表达式计算过程中涉及的所有类型。如果未指定 ,则使用默认值(通常是
在表达式计算期间运行的线程)。ClassLoader
null
ClassLoader
ClassLoader
ClassLoader
ClassLoader
ClassLoader
配置编译器的第二种方法是在 SpEL 嵌入到某些
other 组件,并且可能无法通过配置
对象。在这种情况下,可以通过JVM系统属性(或通过 SpringProperties
机制)将属性设置为枚举值(、 或 )之一。spring.expression.compiler.mode
SpelCompilerMode
off
immediate
mixed