(#expressions)4. Spring Expression Language (SpEL)

预计阅读时间: 54 分钟

4. Spring 表达式语言(SpEL)

The Spring Expression Language (“SpEL” for short) is a powerful expression language that supports querying and manipulating an object graph at runtime.
Spring 表达式语言(简称“SpEL”)是一种强大的表达式语言,支持在运行时查询和操作对象图。
The language syntax is similar to Unified EL but offers additional features, most notably method invocation and basic string templating functionality.
语言语法类似于统一 EL,但提供了额外的功能,最显著的是方法调用和基本字符串模板功能。

While there are several other Java expression languages available — OGNL, MVEL, and JBoss EL, to name a few — the Spring Expression Language was created to provide the Spring community with a single well supported expression language that can be used across all the products in the Spring portfolio.
尽管有几种其他的 Java 表达式语言可用——例如 OGNL、MVEL 和 JBoss EL——Spring 表达式语言被创建出来,旨在为 Spring 社区提供一个单一且得到良好支持的通用表达式语言,该语言可用于 Spring 产品组合中的所有产品。
Its language features are driven by the requirements of the projects in the Spring portfolio, including tooling requirements for code completion support within the Spring Tools for Eclipse. That said, SpEL is based on a technology-agnostic API that lets other expression language implementations be integrated, should the need arise.
其语言特性由 Spring 投资组合中项目的需求驱动,包括对 Spring Tools for Eclipse 中代码补全支持的工具要求。话虽如此,SpEL 基于一个技术无关的 API,允许在需要时集成其他表达式语言实现。

While SpEL serves as the foundation for expression evaluation within the Spring portfolio, it is not directly tied to Spring and can be used independently. To be self contained, many of the examples in this chapter use SpEL as if it were an independent expression language.
虽然 SpEL 是 Spring 系列中表达式评估的基础,但它并不直接与 Spring 绑定,可以独立使用。为了自包含,本章中的许多示例都像使用独立的表达式语言一样使用 SpEL。
This requires creating a few bootstrapping infrastructure classes, such as the parser. Most Spring users need not deal with this infrastructure and can, instead, author only expression strings for evaluation.
这需要创建几个引导基础设施类,例如解析器。大多数 Spring 用户无需处理此基础设施,而只需编写用于评估的表达式字符串即可。
An example of this typical use is the integration of SpEL into creating XML or annotation-based bean definitions, as shown in Expression support for defining bean definitions.
这是一个此类典型用法的示例,即将 SpEL 集成到创建基于 XML 或注解的 bean 定义中,如表达式支持定义 bean 定义所示。

This chapter covers the features of the expression language, its API, and its language syntax. In several places, Inventor and Society classes are used as the target objects for expression evaluation. These class declarations and the data used to populate them are listed at the end of the chapter.
本章涵盖了表达式语言的功能、其 API 及其语言语法。在几个地方,使用 InventorSociety 类作为表达式评估的目标对象。这些类声明以及用于填充它们的数据列在章节末尾。

The expression language supports the following functionality:
表达式语言支持以下功能:

  • Literal expressions 文字表达式

  • Boolean and relational operators
    布尔和关系运算符

  • Regular expressions 正则表达式

  • Class expressions 类表达式

  • Accessing properties, arrays, lists, and maps
    访问属性、数组、列表和映射

  • Method invocation 方法调用

  • Relational operators 关系运算符

  • Assignment 任务

  • Calling constructors 调用构造函数

  • Bean references 豆引用

  • Array construction 数组构造

  • Inline lists 行内列表

  • Inline maps 行内地图

  • Ternary operator 三元运算符

  • Variables 变量

  • User-defined functions 用户定义函数

  • Collection projection 集合投影

  • Collection selection 收藏选择

  • Templated expressions 模板表达式

(#expressions-evaluation)4.1. Evaluation 4.1. 评估

This section introduces the simple use of SpEL interfaces and its expression language. The complete language reference can be found in Language Reference.
本节介绍了 SpEL 接口的简单使用及其表达式语言。完整的语言参考可以在语言参考中找到。

The following code introduces the SpEL API to evaluate the literal string expression, Hello World.
以下代码介绍了 SpEL API 来评估字面字符串表达式, Hello World

ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'"); (1) String message = (String) exp.getValue();

1

The value of the message variable is 'Hello World'.
消息变量的值为 'Hello World'

val parser = SpelExpressionParser() val exp = parser.parseExpression("'Hello World'") (1) val message = exp.value as String

1

The value of the message variable is 'Hello World'.

The SpEL classes and interfaces you are most likely to use are located in the org.springframework.expression package and its sub-packages, such as spel.support.
SpEL 类和接口您最可能使用的是位于 org.springframework.expression 包及其子包中,例如 spel.support

The ExpressionParser interface is responsible for parsing an expression string. In the preceding example, the expression string is a string literal denoted by the surrounding single quotation marks. The Expression interface is responsible for evaluating the previously defined expression string. Two exceptions that can be thrown, ParseException and EvaluationException, when calling parser.parseExpression and exp.getValue, respectively.
ExpressionParser 接口负责解析表达式字符串。在上一个示例中,表达式字符串是一个由周围单引号表示的字符串字面量。 Expression 接口负责评估之前定义的表达式字符串。在分别调用 parser.parseExpressionexp.getValue 时,可能会抛出 ParseExceptionEvaluationException 两个异常。

SpEL supports a wide range of features, such as calling methods, accessing properties, and calling constructors.
SpEL 支持广泛的功能,例如调用方法、访问属性和调用构造函数。

In the following example of method invocation, we call the concat method on the string literal:
在以下方法调用的示例中,我们在字符串字面量上调用 concat 方法:

ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'.concat('!')"); (1) String message = (String) exp.getValue();

1

The value of message is now 'Hello World!'.
message 的值现在是'Hello World!'。

val parser = SpelExpressionParser() val exp = parser.parseExpression("'Hello World'.concat('!')") (1) val message = exp.value as String

1

The value of message is now 'Hello World!'.

The following example of calling a JavaBean property calls the String property Bytes:
以下调用 JavaBean 属性的示例调用的是 String 属性 Bytes

ExpressionParser parser = new SpelExpressionParser(); // invokes 'getBytes()' Expression exp = parser.parseExpression("'Hello World'.bytes"); (1) byte bytes = (byte) exp.getValue();

1

This line converts the literal to a byte array.
这一行将文本转换为字节数组。

val parser = SpelExpressionParser() // invokes 'getBytes()' val exp = parser.parseExpression("'Hello World'.bytes") (1) val bytes = exp.value as ByteArray

1

This line converts the literal to a byte array.

SpEL also supports nested properties by using the standard dot notation (such as prop1.prop2.prop3) and also the corresponding setting of property values. Public fields may also be accessed.
SpEL 也支持使用标准点符号(如 prop1.prop2.prop3 )进行嵌套属性,并且还可以设置属性值。公共字段也可以访问。

The following example shows how to use dot notation to get the length of a literal:
以下示例展示了如何使用点符号来获取字面量的长度:

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 gives the length of the literal.
'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 gives the length of the literal.

The String’s constructor can be called instead of using a string literal, as the following example shows:
字符串构造函数可以代替使用字符串字面量,如下例所示:

ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); (1) String message = exp.getValue(String.class);

1

Construct a new String from the literal and make it be upper case.
构建一个新的 String ,并将其转换为大写。

val parser = SpelExpressionParser() val exp = parser.parseExpression("new String('hello world').toUpperCase()") (1) val message = exp.getValue(String::class.java)

1

Construct a new String from the literal and make it be upper case.

Note the use of the generic method: public <T> T getValue(Class<T> desiredResultType). Using this method removes the need to cast the value of the expression to the desired result type. An EvaluationException is thrown if the value cannot be cast to the type T or converted by using the registered type converter.

The more common usage of SpEL is to provide an expression string that is evaluated against a specific object instance ( called the root object). The following example shows how to retrieve the name property from an instance of the Inventor class or create a boolean condition:

// 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

(#expressions-evaluation-context)4.1.1. UnderstandingEvaluationContext 4.1.1. 理解EvaluationContext

The EvaluationContext interface is used when evaluating an expression to resolve properties, methods, or fields and to help perform type conversion. Spring provides two implementations.
EvaluationContext 接口用于在评估表达式时解析属性、方法或字段,并帮助执行类型转换。Spring 提供了两种实现。

  • SimpleEvaluationContext: Exposes a subset of essential SpEL language features and configuration options, for categories of expressions that do not require the full extent of the SpEL language syntax and should be meaningfully restricted.
    SimpleEvaluationContext :暴露了 SpEL 语言功能集的一部分和配置选项,用于不需要 SpEL 语言完整语法的表达式类别,应进行有意义的限制。
    Examples include but are not limited to data binding expressions and property-based filters.
    示例包括但不限于数据绑定表达式和基于属性的过滤器。

  • StandardEvaluationContext: Exposes the full set of SpEL language features and configuration options. You can use it to specify a default root object and to configure every available evaluation-related strategy.
    StandardEvaluationContext :公开了 SpEL 语言的全部特性和配置选项。您可以使用它来指定默认根对象,并配置每个可用的评估相关策略。

SimpleEvaluationContext is designed to support only a subset of the SpEL language syntax. It excludes Java type references, constructors, and bean references. It also requires you to explicitly choose the level of support for properties and methods in expressions. By default, the create() static factory method enables only read access to properties. You can also obtain a builder to configure the exact level of support needed, targeting one or some combination of the following:
SimpleEvaluationContext 仅支持 SpEL 语言语法的子集。它不包括 Java 类型引用、构造函数和 bean 引用。它还要求您在表达式中显式选择对属性和方法的支持级别。默认情况下, create() 静态工厂方法仅启用对属性的读取访问。您还可以获取一个构建器来配置所需的确切支持级别,针对以下一个或多个组合:

  • Custom PropertyAccessor only (no reflection)
    自定义 PropertyAccessor 仅(无反射)

  • Data binding properties for read-only access
    数据绑定属性,仅限只读访问

  • Data binding properties for read and write
    数据绑定属性,用于读取和写入

(#expressions-type-conversion)Type Conversion 类型转换

By default, SpEL uses the conversion service available in Spring core ( org.springframework.core.convert.ConversionService). This conversion service comes with many built-in converters for common conversions but is also fully extensible so that you can add custom conversions between types. Additionally, it is generics-aware.
默认情况下,SpEL 使用 Spring 核心中可用的转换服务( org.springframework.core.convert.ConversionService )。此转换服务包含许多内置的转换器,用于常见的转换,但同时也完全可扩展,以便您可以在类型之间添加自定义转换。此外,它还支持泛型。
This means that, when you work with generic types in expressions, SpEL attempts conversions to maintain type correctness for any objects it encounters.
这意味着,当你在表达式中使用泛型类型时,SpEL 会尝试转换以保持遇到任何对象时的类型正确性。

What does this mean in practice? Suppose assignment, using setValue(), is being used to set a List property. The type of the property is actually List<Boolean>. SpEL recognizes that the elements of the list need to be converted to Boolean before being placed in it. The following example shows how to do so:
实际上这意味着什么?假设使用 setValue() 进行赋值,用于设置 List 属性。属性的类型实际上是 List<Boolean> 。SpEL 识别出在将其放入之前,列表的元素需要转换为 Boolean 。以下示例展示了如何进行转换:

class Simple { public List<Boolean> booleanList = new ArrayList<Boolean>(); } 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]

(#expressions-parser-configuration)4.1.2. Parser Configuration

4.1.2. 解析器配置

It is possible to configure the SpEL expression parser by using a parser configuration object ( org.springframework.expression.spel.SpelParserConfiguration). The configuration object controls the behavior of some of the expression components. For example, if you index into an array or collection and the element at the specified index is null, SpEL can automatically create the element. This is useful when using expressions made up of a chain of property references.
可以通过使用解析器配置对象( org.springframework.expression.spel.SpelParserConfiguration )来配置 SpEL 表达式解析器。配置对象控制某些表达式组件的行为。例如,如果您在数组或集合中索引并指定索引处的元素是 null ,SpEL 可以自动创建该元素。这在使用由属性引用链组成的表达式时非常有用。
If you index into an array or list and specify an index that is beyond the end of the current size of the array or list, SpEL can automatically grow the array or list to accommodate that index.
如果您索引到一个数组或列表并指定一个超出当前数组或列表大小的索引,SpEL 可以自动扩展数组或列表以容纳该索引。
In order to add an element at the specified index, SpEL will try to create the element using the element type’s default constructor before setting the specified value. If the element type does not have a default constructor, null will be added to the array or list. If there is no built-in or custom converter that knows how to set the value, null will remain in the array or list at the specified index. The following example demonstrates how to automatically grow the list:
为了在指定索引处添加元素,SpEL 将尝试使用元素类型的默认构造函数创建元素,然后再设置指定的值。如果元素类型没有默认构造函数,将在数组或列表中添加 null 。如果没有内置或自定义转换器知道如何设置值, null 将保留在数组或列表的指定索引处。以下示例演示了如何自动扩展列表:

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

(#expressions-spel-compilation)4.1.3. SpEL Compilation 4.1.3. SpEL 编译

Spring Framework 4.1 includes a basic expression compiler. Expressions are usually interpreted, which provides a lot of dynamic flexibility during evaluation but does not provide optimum performance.
Spring Framework 4.1 包含一个基本的表达式编译器。表达式通常被解释,这在评估期间提供了很多动态灵活性,但并不提供最佳性能。
For occasional expression usage, this is fine, but, when used by other components such as Spring Integration, performance can be very important, and there is no real need for the dynamism.
偶尔使用表达方式,这没问题,但是当其他组件如 Spring Integration 使用时,性能可能非常重要,实际上并不需要真正的动态性。

The SpEL compiler is intended to address this need. During evaluation, the compiler generates a Java class that embodies the expression behavior at runtime and uses that class to achieve much faster expression evaluation.
SpEL 编译器旨在满足这一需求。在评估过程中,编译器生成一个 Java 类,该类体现了表达式在运行时的行为,并使用该类实现更快的表达式评估。
Due to the lack of typing around expressions, the compiler uses information gathered during the interpreted evaluations of an expression when performing compilation.
由于缺乏围绕表达式的打字,编译器在执行编译时使用在表达式解释评估期间收集到的信息。
For example, it does not know the type of a property reference purely from the expression, but during the first interpreted evaluation, it finds out what it is.
例如,它不能仅从表达式中知道属性引用的类型,但在第一次解释性评估期间,它会找出它的类型。
Of course, basing compilation on such derived information can cause trouble later if the types of the various expression elements change over time. For this reason, compilation is best suited to expressions whose type information is not going to change on repeated evaluations.
当然,如果各种表达式元素的类型随时间变化,基于这种派生信息进行编译可能会在以后造成麻烦。因此,编译最适合那些在重复评估中类型信息不会改变的表达式。

Consider the following basic expression:
考虑以下基本表达式:

someArray[0].someProperty.someOtherProperty < 0.1

Because the preceding expression involves array access, some property de-referencing, and numeric operations, the performance gain can be very noticeable.
因为前面的表达式涉及数组访问、一些属性解引用和数值运算,性能提升可能非常明显。
In an example micro benchmark run of 50000 iterations, it took 75ms to evaluate by using the interpreter and only 3ms using the compiled version of the expression.
在一个包含 50000 次迭代的示例微基准测试中,使用解释器评估耗时 75 毫秒,而使用编译后的表达式版本仅需 3 毫秒。

(#expressions-compiler-configuration)Compiler Configuration 编译器配置

The compiler is not turned on by default, but you can turn it on in either of two different ways. You can turn it on by using the parser configuration process (discussed earlier) or by using a Spring property when SpEL usage is embedded inside another component. This section discusses both of these options.
编译器默认未开启,但您可以通过两种不同的方式将其开启。您可以通过使用解析器配置过程(前面已讨论)或在使用 SpEL 嵌入到另一个组件时使用 Spring 属性来开启它。本节将讨论这两种选项。

The compiler can operate in one of three modes, which are captured in the org.springframework.expression.spel.SpelCompilerMode enum. The modes are as follows:
编译器可以在三种模式之一中运行,这些模式在 org.springframework.expression.spel.SpelCompilerMode 枚举中被捕获。模式如下:

  • OFF (default): The compiler is switched off.
    OFF (默认):编译器已关闭。

  • IMMEDIATE: In immediate mode, the expressions are compiled as soon as possible. This is typically after the first interpreted evaluation.
    IMMEDIATE : 在立即模式下,表达式会尽快编译。这通常是在第一次解释评估之后。
    If the compiled expression fails (typically due to a type changing, as described earlier), the caller of the expression evaluation receives an exception.
    如果编译的表达式失败(通常是由于类型改变,如前所述),调用表达式评估的调用者会收到一个异常。

  • MIXED: In mixed mode, the expressions silently switch between interpreted and compiled mode over time.
    MIXED :在混合模式下,表达式会随着时间的推移在解释模式和编译模式之间静默切换。
    After some number of interpreted runs, they switch to compiled form and, if something goes wrong with the compiled form (such as a type changing, as described earlier), the expression automatically switches back to interpreted form again.
    在经过一定数量的解释执行后,它们切换到编译形式,如果编译形式出现问题(如类型改变,如前所述),表达式会自动切换回解释形式。
    Sometime later, it may generate another compiled form and switch to it. Basically, the exception that the user gets in IMMEDIATE mode is instead handled internally.
    稍后,它可能生成另一种编译形式并切换到它。基本上,用户在 IMMEDIATE 模式下遇到的异常将由内部处理。

IMMEDIATE mode exists because MIXED mode could cause issues for expressions that have side effects. If a compiled expression blows up after partially succeeding, it may have already done something that has affected the state of the system.
IMMEDIATE 模式存在是因为 MIXED 模式可能会对具有副作用的表达式造成问题。如果编译后的表达式在部分成功后崩溃,它可能已经做了某些影响系统状态的事情。
If this has happened, the caller may not want it to silently re-run in interpreted mode, since part of the expression may be running twice.
如果发生这种情况,调用者可能不希望它在解释模式下静默重新运行,因为表达式的一部分可能运行了两次。

After selecting a mode, use the SpelParserConfiguration to configure the parser. The following example shows how to do so:
选择模式后,使用 SpelParserConfiguration 配置解析器。以下示例展示了如何操作:

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)

When you specify the compiler mode, you can also specify a classloader (passing null is allowed). Compiled expressions are defined in a child classloader created under any that is supplied.
当您指定编译器模式时,也可以指定一个类加载器(允许传递 null)。编译表达式定义在为任何提供的类加载器下创建的子类加载器中。
It is important to ensure that, if a classloader is specified, it can see all the types involved in the expression evaluation process.
确保如果指定了类加载器,它可以看到表达式评估过程中涉及的所有类型,这是很重要的。
If you do not specify a classloader, a default classloader is used (typically the context classloader for the thread that is running during expression evaluation).
如果您未指定类加载器,则使用默认类加载器(通常是表达式评估期间运行的线程的上下文类加载器)。

The second way to configure the compiler is for use when SpEL is embedded inside some other component and it may not be possible to configure it through a configuration object. In these cases, it is possible to set the spring.expression.compiler.mode property via a JVM system property (or via the SpringProperties mechanism) to one of the SpelCompilerMode enum values (off, immediate, or mixed).
第二种配置编译器的方式是在 SpEL 嵌入到其他组件内部,可能无法通过配置对象进行配置时使用。在这种情况下,可以通过 JVM 系统属性(或通过 SpringProperties 机制)将 spring.expression.compiler.mode 属性设置为 SpelCompilerMode 枚举值之一( offimmediatemixed )。

(#expressions-compiler-limitations)Compiler Limitations 编译器限制

Since Spring Framework 4.1, the basic compilation framework is in place. However, the framework does not yet support compiling every kind of expression. The initial focus has been on the common expressions that are likely to be used in performance-critical contexts.
自 Spring 框架 4.1 以来,基本的编译框架已经建立。然而,该框架尚不支持编译所有类型的表达式。最初的焦点一直集中在可能在性能关键环境中使用的常见表达式上。
The following kinds of expression cannot be compiled at the moment:
以下这类表达式目前无法编译:

  • Expressions involving assignment
    涉及赋值的表达式

  • Expressions relying on the conversion service
    依赖于转换服务的表达式

  • Expressions using custom resolvers or accessors
    使用自定义解析器或访问器表达式

  • Expressions using selection or projection
    选择或投影的表达式

More types of expressions will be compilable in the future.
未来将支持更多类型的表达式进行编译。

(#expressions-beandef)4.2. Expressions in Bean Definitions

4.2. 在 Bean 定义中的表达式

You can use SpEL expressions with XML-based or annotation-based configuration metadata for defining BeanDefinition instances. In both cases, the syntax to define the expression is of the form #{ <expression string> }.
您可以使用基于 XML 或基于注解的配置元数据与 SpEL 表达式来定义 BeanDefinition 实例。在这两种情况下,定义表达式的语法形式为 #{ <expression string> }

(#expressions-beandef-xml-based)4.2.1. XML Configuration

4.2.1. XML 配置

A property or constructor argument value can be set by using expressions, as the following example shows:
属性或构造函数参数值可以通过使用表达式来设置,如下例所示:

<bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties --> </bean>

All beans in the application context are available as predefined variables with their common bean name. This includes standard context beans such as environment (of type org.springframework.core.env.Environment) as well as systemProperties and systemEnvironment (of type Map<String, Object>) for access to the runtime environment.
所有应用程序上下文中的豆类都作为预定义变量可用,其名称为通用豆名。这包括标准上下文豆类,如 environment (类型为 org.springframework.core.env.Environment ),以及用于访问运行时环境的 systemPropertiessystemEnvironment (类型为 Map<String, Object> )。

The following example shows access to the systemProperties bean as a SpEL variable:
以下示例展示了如何将 systemProperties bean 作为 SpEL 变量进行访问:

<bean id="taxCalculator" class="org.spring.samples.TaxCalculator"> <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/> <!-- other properties --> </bean>

Note that you do not have to prefix the predefined variable with the # symbol here.
注意,在这里您不需要在预定义变量前加上 # 符号。

You can also refer to other bean properties by name, as the following example shows:
您也可以通过名称引用其他 bean 属性,如下例所示:

<bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties --> </bean> <bean id="shapeGuess" class="org.spring.samples.ShapeGuess"> <property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> <!-- other properties --> </bean>

(#expressions-beandef-annotation-based)4.2.2. Annotation Configuration

4.2.2. 注解配置

To specify a default value, you can place the @Value annotation on fields, methods, and method or constructor parameters.
为了指定默认值,您可以在字段、方法和方法或构造函数参数上放置 @Value 注释。

The following example sets the default value of a field:
以下示例设置字段的默认值:

public class FieldValueTestBean { @Value("#{ systemProperties['user.region'] }") private String defaultLocale; public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } }
class FieldValueTestBean { @Value("#{ systemProperties['user.region'] }") var defaultLocale: String? = null }

The following example shows the equivalent but on a property setter method:
以下示例显示了在属性设置器方法中的等效操作:

public class PropertyValueTestBean { private String defaultLocale; @Value("#{ systemProperties['user.region'] }") public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } }
class PropertyValueTestBean { @Value("#{ systemProperties['user.region'] }") var defaultLocale: String? = null }

Autowired methods and constructors can also use the @Value annotation, as the following examples show:
自动注入的方法和构造函数也可以使用 @Value 注解,如下例所示:

public class SimpleMovieLister { private MovieFinder movieFinder; private String defaultLocale; @Autowired public void configure(MovieFinder movieFinder, @Value("#{ systemProperties['user.region'] }") String defaultLocale) { this.movieFinder = movieFinder; this.defaultLocale = defaultLocale; } // ... }
class SimpleMovieLister { private lateinit var movieFinder: MovieFinder private lateinit var defaultLocale: String @Autowired fun configure(movieFinder: MovieFinder, @Value("#{ systemProperties['user.region'] }") defaultLocale: String) { this.movieFinder = movieFinder this.defaultLocale = 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; } // ... }
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao, @Value("#{systemProperties['user.country']}") private val defaultLocale: String) { // ... }

(#expressions-language-ref)4.3. Language Reference 4.3. 语言参考

This section describes how the Spring Expression Language works. It covers the following topics:
本节描述了 Spring 表达式语言的工作原理。它涵盖了以下主题:

(#expressions-ref-literal)4.3.1. Literal Expressions

4.3.1. 文字表达式

SpEL supports the following types of literal expressions.
SpEL 支持以下类型的字面量表达式。

  • strings 字符串

  • numeric values: integer (int or long), hexadecimal (int or long), real (float or double)
    数值:整数( intlong ),十六进制( intlong ),实数( floatdouble

  • boolean values: true or false
    布尔值: truefalse

  • null

Strings can delimited by single quotation marks (') or double quotation marks ("). To include a single quotation mark within a string literal enclosed in single quotation marks, use two adjacent single quotation mark characters.
字符串可以用单引号( ' )或双引号( " )分隔。要在单引号括起来的字符串字面量中包含单引号,请使用两个相邻的单引号字符。
Similarly, to include a double quotation mark within a string literal enclosed in double quotation marks, use two adjacent double quotation mark characters.
同样,要在双引号内的字符串字面量中包含一个双引号,请使用两个相邻的双引号字符。

Numbers support the use of the negative sign, exponential notation, and decimal points. By default, real numbers are parsed by using Double.parseDouble().
数字支持使用负号、指数表示法和小数点。默认情况下,实数通过使用 Double.parseDouble() 进行解析。

The following listing shows simple usage of literals. Typically, they are not used in isolation like this but, rather, as part of a more complex expression — for example, using a literal on one side of a logical comparison operator or as an argument to a method.
以下列表展示了字面量的简单用法。通常,它们不会像这样单独使用,而是作为更复杂表达式的一部分——例如,在逻辑比较运算符的一侧使用字面量,或将字面量作为方法参数。

ExpressionParser parser = new SpelExpressionParser(); // evaluates to "Hello World" String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evaluates to "Tony's Pizza" String pizzaParlor = (String) parser.parseExpression("'Tony''s Pizza'").getValue(); double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); // evaluates to 2147483647 int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); Object nullValue = parser.parseExpression("null").getValue();
val parser = SpelExpressionParser() // evaluates to "Hello World" val helloWorld = parser.parseExpression("'Hello World'").value as String // evaluates to "Tony's Pizza" val pizzaParlor = parser.parseExpression("'Tony''s Pizza'").value as String val avogadrosNumber = parser.parseExpression("6.0221415E+23").value as Double // evaluates to 2147483647 val maxValue = parser.parseExpression("0x7FFFFFFF").value as Int val trueValue = parser.parseExpression("true").value as Boolean val nullValue = parser.parseExpression("null").value

(#expressions-properties-arrays)4.3.2. Properties, Arrays, Lists, Maps, and Indexers

4.3.2. 属性、数组、列表、映射和索引器

Navigating with property references is easy. To do so, use a period to indicate a nested property value. The instances of the Inventor class, pupin and tesla, were populated with data listed in the Classes used in the examples section. To navigate "down" the object graph and get Tesla’s year of birth and Pupin’s city of birth, we use the following expressions:
使用属性引用进行导航很简单。要做到这一点,使用点号来表示嵌套属性值。 Inventor 类的实例 pupintesla 被填充了在示例部分中使用的类中列出的数据。要导航对象图“向下”并获取特斯拉的出生年份和普平的出生城市,我们使用以下表达式:

// evaluates to 1856 int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context); String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
// evaluates to 1856 val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String

Case insensitivity is allowed for the first letter of property names. Thus, the expressions in the above example may be written as Birthdate.Year + 1900 and PlaceOfBirth.City, respectively. In addition, properties may optionally be accessed via method invocations — for example, getPlaceOfBirth().getCity() instead of placeOfBirth.city.
属性名称的首字母不区分大小写。因此,上述示例中的表达式可以分别写成 Birthdate.Year + 1900PlaceOfBirth.City 。此外,属性可以通过方法调用可选地访问——例如,使用 getPlaceOfBirth().getCity() 而不是 placeOfBirth.city

The contents of arrays and lists are obtained by using square bracket notation, as the following example shows:
数组与列表的内容通过使用方括号表示法获取,如下例所示:

ExpressionParser 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);
val parser = SpelExpressionParser() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() // Inventions Array // evaluates to "Induction motor" val invention = parser.parseExpression("inventions[3]").getValue( context, tesla, String::class.java) // Members List // evaluates to "Nikola Tesla" val name = parser.parseExpression("members[0].name").getValue( context, ieee, String::class.java) // List and Array navigation // evaluates to "Wireless communication" val invention = parser.parseExpression("members[0].inventions[6]").getValue( context, ieee, String::class.java)

The contents of maps are obtained by specifying the literal key value within the brackets. In the following example, because keys for the officers map are strings, we can specify string literals:
地图内容通过在括号内指定文字键值来获取。在以下示例中,因为 officers 地图的键是字符串,我们可以指定字符串字面量:

// 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");
// Officer's Dictionary val pupin = parser.parseExpression("officers['president']").getValue( societyContext, Inventor::class.java) // evaluates to "Idvor" val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue( societyContext, String::class.java) // setting values parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue( societyContext, "Croatia")

(#expressions-inline-lists)4.3.3. Inline Lists 4.3.3. 行内列表

You can directly express lists in an expression by using {} notation.
您可以直接使用 {} 符号在表达式中表示列表。

// 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);
// evaluates to a Java list containing the four numbers val numbers = parser.parseExpression("{1,2,3,4}").getValue(context) as List<*> val listOfLists = parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context) as List<*>

{} by itself means an empty list. For performance reasons, if the list is itself entirely composed of fixed literals, a constant list is created to represent the expression (rather than building a new list on each evaluation).
{} 本身表示一个空列表。出于性能考虑,如果列表本身完全由固定字面量组成,则会创建一个常量列表来表示该表达式(而不是在每次评估时构建一个新的列表)。

(#expressions-inline-maps)4.3.4. Inline Maps 4.3.4. 行内地图

You can also directly express maps in an expression by using {key:value} notation. The following example shows how to do so:
您也可以使用 {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);
// evaluates to a Java map containing the two entries val inventorInfo = parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, *> val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<*, *>

{:} by itself means an empty map. For performance reasons, if the map is itself composed of fixed literals or other nested constant structures (lists or maps), a constant map is created to represent the expression (rather than building a new map on each evaluation).
{:} 本身表示一个空映射。出于性能考虑,如果映射本身由固定字面量或其他嵌套常量结构(列表或映射)组成,则创建一个常量映射来表示该表达式(而不是在每次评估时构建一个新的映射)。
Quoting of the map keys is optional (unless the key contains a period (.)). The examples above do not use quoted keys.
引用地图键是可选的(除非键包含点( . ))。上面的示例没有使用引号键。

(#expressions-array-construction)4.3.5. Array Construction

4.3.5. 数组构造

You can build arrays by using the familiar Java syntax, optionally supplying an initializer to have the array populated at construction time. The following example shows how to do so:
您可以使用熟悉的 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);
val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray // Array with initializer val numbers2 = parser.parseExpression("new int{1,2,3}").getValue(context) as IntArray // Multi dimensional array val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray>

You cannot currently supply an initializer when you construct a multi-dimensional array.
您目前无法在构造多维数组时提供初始化器。

(#expressions-methods)4.3.6. Methods 4.3.6. 方法

You can invoke methods by using typical Java programming syntax. You can also invoke methods on literals. Variable arguments are also supported. The following examples show how to invoke methods:
您可以使用典型的 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);
// string literal, evaluates to "bc" val bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String::class.java) // evaluates to true val isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( societyContext, Boolean::class.java)

(#expressions-operators)4.3.7. Operators 4.3.7. 运算符

The Spring Expression Language supports the following kinds of operators:
Spring 表达式语言支持以下类型的操作符:

(#expressions-operators-relational)Relational Operators 关系运算符

The relational operators (equal, not equal, less than, less than or equal, greater than, and greater than or equal) are supported by using standard operator notation. The following listing shows a few examples of operators:
关系运算符(等于、不等于、小于、小于等于、大于、大于等于)通过使用标准运算符符号支持。以下列表展示了几个运算符的示例:

// 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);
// evaluates to true val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java) // evaluates to false val falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean::class.java) // evaluates to true val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)

Greater-than and less-than comparisons against null follow a simple rule: null is treated as nothing (that is NOT as zero). As a consequence, any other value is always greater than null (X > null is always true) and no other value is ever less than nothing (X < null is always false).
大于和小于比较遵循简单规则: null 被视为无(即不是零)。因此,任何其他值总是大于 nullX > null 总是 true ),并且没有其他值会小于无( X < null 总是 false )。

If you prefer numeric comparisons instead, avoid number-based null comparisons in favor of comparisons against zero ( for example, X > 0 or X < 0).
如果您更喜欢数字比较,请避免基于数字的 null 比较,而应选择与零的比较(例如, X > 0X < 0 )。

In addition to the standard relational operators, SpEL supports the instanceof and regular expression-based matches operator. The following listing shows examples of both:
除了标准的比较运算符外,SpEL 还支持基于 instanceof 和基于正则表达式的 matches 运算符。以下列表展示了这两个运算符的示例:

// 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);
// evaluates to false val falseValue = parser.parseExpression( "'xyz' instanceof T(Integer)").getValue(Boolean::class.java) // evaluates to true val trueValue = parser.parseExpression( "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java) // evaluates to false val falseValue = parser.parseExpression( "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

Be careful with primitive types, as they are immediately boxed up to their wrapper types. For example, 1 instanceof T(int) evaluates to false, while 1 instanceof T(Integer) evaluates to true, as expected.
小心原始类型,因为它们会立即装箱成它们的包装类型。例如, 1 instanceof T(int) 等于 false ,而 1 instanceof T(Integer) 等于 true ,正如预期的那样。

Each symbolic operator can also be specified as a purely alphabetic equivalent. This avoids problems where the symbols used have special meaning for the document type in which the expression is embedded (such as in an XML document). The textual equivalents are:
每个符号运算符也可以指定为纯字母等效。这避免了在表达式嵌入的文档类型(如 XML 文档)中使用的符号具有特殊意义的问题。文本等效为:

  • lt (<)lt<

  • gt (>)gt>

  • le (<=)le<=

  • ge (>=)ge>=

  • eq (==)eq==

  • ne (!=)ne!=

  • div (/)div/

  • mod (%)mod%

  • not (!).not! )。

All of the textual operators are case-insensitive.
所有文本操作符均不区分大小写。

(#expressions-operators-logical)Logical Operators 逻辑运算符

SpEL supports the following logical operators:
SpEL 支持以下逻辑运算符:

  • and (&&)and&&

  • or (||)or||

  • not (!)not!

The following example shows how to use the logical operators:
以下示例展示了如何使用逻辑运算符:

// -- 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);
// -- AND -- // evaluates to false val falseValue = parser.parseExpression("true and false").getValue(Boolean::class.java) // evaluates to true val expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')" val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java) // -- OR -- // evaluates to true val trueValue = parser.parseExpression("true or false").getValue(Boolean::class.java) // evaluates to true val expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')" val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java) // -- NOT -- // evaluates to false val falseValue = parser.parseExpression("!true").getValue(Boolean::class.java) // -- AND and NOT -- val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')" val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

(#expressions-operators-mathematical)Mathematical Operators 数学运算符

You can use the addition operator (+) on both numbers and strings. You can use the subtraction (-), multiplication ( *), and division (/) operators only on numbers. You can also use the modulus (%) and exponential power (^) operators on numbers. Standard operator precedence is enforced. The following example shows the mathematical operators in use:
您可以在数字和字符串上使用加法运算符( + )。您只能在数字上使用减法( - )、乘法( * )和除法( / )运算符。您还可以在数字上使用取模( % )和指数幂( ^ )运算符。标准运算符优先级得到执行。以下示例显示了使用中的数学运算符:

// 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
// Addition val two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2 val testString = parser.parseExpression( "'test' + ' ' + 'string'").getValue(String::class.java) // 'test string' // Subtraction val four = parser.parseExpression("1 - -3").getValue(Int::class.java) // 4 val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class.java) // -9000 // Multiplication val six = parser.parseExpression("-2 * -3").getValue(Int::class.java) // 6 val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java) // 24.0 // Division val minusTwo = parser.parseExpression("6 / -3").getValue(Int::class.java) // -2 val one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double::class.java) // 1.0 // Modulus val three = parser.parseExpression("7 % 4").getValue(Int::class.java) // 3 val one = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java) // 1 // Operator precedence val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21

(#expressions-assignment)The Assignment Operator 赋值运算符

To set a property, use the assignment operator (=). This is typically done within a call to setValue but can also be done inside a call to getValue. The following listing shows both ways to use the assignment operator:
设置属性时,请使用赋值运算符( = )。这通常在调用 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);
val inventor = Inventor() val context = SimpleEvaluationContext.forReadWriteDataBinding().build() parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic") // alternatively val aleks = parser.parseExpression( "name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)

(#expressions-types)4.3.8. Types 4.3.8. 类型

You can use the special T operator to specify an instance of java.lang.Class (the type). Static methods are invoked by using this operator as well. The StandardEvaluationContext uses a TypeLocator to find types, and the StandardTypeLocator (which can be replaced) is built with an understanding of the java.lang package. This means that T() references to types within the java.lang package do not need to be fully qualified, but all other type references must be. The following example shows how to use the T operator:
您可以使用特殊的 T 操作符来指定 java.lang.Class (类型)的实例。静态方法也可以通过使用此操作符来调用。 StandardEvaluationContext 使用 TypeLocator 来查找类型,而 StandardTypeLocator (可以替换)是基于对 java.lang 包的理解构建的。这意味着在 java.lang 包内对类型的 T() 引用不需要完全限定,但所有其他类型引用都必须完全限定。以下示例展示了如何使用 T 操作符:

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); boolean trueValue = parser.parseExpression( "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") .getValue(Boolean.class);
val dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class::class.java) val stringClass = parser.parseExpression("T(String)").getValue(Class::class.java) val trueValue = parser.parseExpression( "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") .getValue(Boolean::class.java)

(#expressions-constructors)4.3.9. Constructors 4.3.9. 构造函数

You can invoke constructors by using the new operator. You should use the fully qualified class name for all types except those located in the java.lang package (Integer, Float, String, and so on). The following example shows how to use the new operator to invoke constructors:
您可以使用 new 运算符调用构造函数。对于不在 java.lang 包( IntegerFloatString 等)中的所有类型,您应该使用完全限定的类名。以下示例展示了如何使用 new 运算符调用构造函数:

Inventor 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);
val einstein = p.parseExpression( "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')") .getValue(Inventor::class.java) // 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)

(#expressions-ref-variables)4.3.10. Variables 4.3.10. 变量

You can reference variables in the expression by using the #variableName syntax. Variables are set by using the setVariable method on EvaluationContext implementations.
您可以通过使用 #variableName 语法在表达式中引用变量。变量通过在 EvaluationContext 实现上使用 setVariable 方法来设置。

Valid variable names must be composed of one or more of the following supported characters.
有效的变量名必须由以下支持的字符组成一个或多个。

  • letters: A to Z and a to z
    字母: AZaz

  • digits: 0 to 9数字: 09

  • underscore: _下划线: _

  • dollar sign: $美元符号: $

The following example shows how to use variables.
以下示例展示了如何使用变量。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); context.setVariable("newName", "Mike Tesla"); parser.parseExpression("name = #newName").getValue(context, tesla); System.out.println(tesla.getName()) // "Mike Tesla"
val tesla = Inventor("Nikola Tesla", "Serbian") val context = SimpleEvaluationContext.forReadWriteDataBinding().build() context.setVariable("newName", "Mike Tesla") parser.parseExpression("name = #newName").getValue(context, tesla) println(tesla.name) // "Mike Tesla"

(#expressions-this-root)The#this and#root Variables

#this#root 变量

The #this variable is always defined and refers to the current evaluation object (against which unqualified references are resolved). The #root variable is always defined and refers to the root context object. Although #this may vary as components of an expression are evaluated, #root always refers to the root. The following examples show how to use the #this and #root variables:
#this 变量始终定义,并指向当前评估对象(未限定引用将针对此对象进行解析)。 #root 变量始终定义,并指向根上下文对象。尽管 #this 可能会随着表达式组件的评估而变化,但 #root 总是指向根。以下示例展示了如何使用 #this#root 变量:

// create an array of integers List<Integer> primes = new ArrayList<Integer>(); primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); // create parser and set variable 'primes' as the array of integers ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess(); context.setVariable("primes", primes); // all prime numbers > 10 from the list (using selection ?{...}) // evaluates to [11, 13, 17] List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression( "#primes.?[#this>10]").getValue(context);
// create an array of integers val primes = ArrayList<Int>() primes.addAll(listOf(2, 3, 5, 7, 11, 13, 17)) // create parser and set variable 'primes' as the array of integers val parser = SpelExpressionParser() val context = SimpleEvaluationContext.forReadOnlyDataAccess() context.setVariable("primes", primes) // all prime numbers > 10 from the list (using selection ?{...}) // evaluates to [11, 13, 17] val primesGreaterThanTen = parser.parseExpression( "#primes.?[#this>10]").getValue(context) as List<Int>

(#expressions-ref-functions)4.3.11. Functions 4.3.11. 函数

You can extend SpEL by registering user-defined functions that can be called within the expression string. The function is registered through the EvaluationContext. The following example shows how to register a user-defined function:
您可以通过注册用户定义的函数来扩展 SpEL,这些函数可以在表达式字符串中调用。函数通过 EvaluationContext 进行注册。以下示例展示了如何注册一个用户定义的函数:

Method method = ...; EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("myFunction", method);
val method: Method = ... val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() context.setVariable("myFunction", method)

For example, consider the following utility method that reverses a string:
例如,考虑以下一个反转字符串的实用方法:

public abstract class StringUtils { public static String reverseString(String input) { StringBuilder backwards = new StringBuilder(input.length()); for (int i = 0; i < input.length(); i++) { backwards.append(input.charAt(input.length() - 1 - i)); } return backwards.toString(); } }
fun reverseString(input: String): String { val backwards = StringBuilder(input.length) for (i in 0 until input.length) { backwards.append(input[input.length - 1 - i]) } return backwards.toString() }

You can then register and use the preceding method, as the following example shows:
您可以根据以下示例进行注册和使用上述方法:

ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("reverseString", StringUtils.class.getDeclaredMethod("reverseString", String.class)); String helloWorldReversed = parser.parseExpression( "#reverseString('hello')").getValue(context, String.class);
val parser = SpelExpressionParser() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() context.setVariable("reverseString", ::reverseString::javaMethod) val helloWorldReversed = parser.parseExpression( "#reverseString('hello')").getValue(context, String::class.java)

(#expressions-bean-references)4.3.12. Bean References 4.3.12. 豆引用

If the evaluation context has been configured with a bean resolver, you can look up beans from an expression by using the @ symbol. The following example shows how to do so:
如果评估上下文已配置了 Bean 解析器,您可以通过使用 @ 符号从表达式中查找 Bean。以下示例展示了如何操作:

ExpressionParser 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);
val parser = SpelExpressionParser() val context = StandardEvaluationContext() context.setBeanResolver(MyBeanResolver()) // This will end up calling resolve(context,"something") on MyBeanResolver during evaluation val bean = parser.parseExpression("@something").getValue(context)

To access a factory bean itself, you should instead prefix the bean name with an & symbol. The following example shows how to do so:
要访问工厂 bean 本身,您应该用 & 符号作为 bean 名称的前缀。以下示例展示了如何这样做:

ExpressionParser 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);
val parser = SpelExpressionParser() val context = StandardEvaluationContext() context.setBeanResolver(MyBeanResolver()) // This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation val bean = parser.parseExpression("&foo").getValue(context)

(#expressions-operator-ternary)4.3.13. Ternary Operator (If-Then-Else)

4.3.13. 三元运算符(如果-那么-否则)

You can use the ternary operator for performing if-then-else conditional logic inside the expression. The following listing shows a minimal example:
您可以使用三元运算符在表达式中执行 if-then-else 条件逻辑。以下列表显示了一个最小示例:

String falseString = parser.parseExpression( "false ? 'trueExp' : 'falseExp'").getValue(String.class);
val falseString = parser.parseExpression( "false ? 'trueExp' : 'falseExp'").getValue(String::class.java)

In this case, the boolean false results in returning the string value 'falseExp'. A more realistic example follows:
在这种情况下,布尔值 false 导致返回字符串值 'falseExp' 。以下是一个更现实的例子:

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"
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'" val queryResultString = parser.parseExpression(expression) .getValue(societyContext, String::class.java) // queryResultString = "Nikola Tesla is a member of the IEEE Society"

See the next section on the Elvis operator for an even shorter syntax for the ternary operator.
查看下一节关于 Elvis 操作符的部分,以获取三元操作符的更短语法。

(#expressions-operator-elvis)4.3.14. The Elvis Operator

4.3.14. Elvis 操作符

The Elvis operator is a shortening of the ternary operator syntax and is used in the Groovy language. With the ternary operator syntax, you usually have to repeat a variable twice, as the following example shows:
Elvis 运算符是三元运算符语法的缩写,用于 Groovy 语言中。使用三元运算符语法时,通常需要重复一个变量两次,如下例所示:

String name = "Elvis Presley"; String displayName = (name != null ? name : "Unknown");

Instead, you can use the Elvis operator (named for the resemblance to Elvis' hair style). The following example shows how to use the Elvis operator:
相反,您可以使用 Elvis 运算符(因其与猫王发型相似而得名)。以下示例展示了如何使用 Elvis 运算符:

ExpressionParser parser = new SpelExpressionParser(); String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class); System.out.println(name); // 'Unknown'
val parser = SpelExpressionParser() val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java) println(name) // 'Unknown'

The following listing shows a more complex example:
以下列表显示了一个更复杂的示例:

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
val parser = SpelExpressionParser() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val tesla = Inventor("Nikola Tesla", "Serbian") var name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java) println(name) // Nikola Tesla tesla.setName(null) name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java) println(name) // Elvis Presley

You can use the Elvis operator to apply default values in expressions. The following example shows how to use the Elvis operator in a @Value expression:
您可以使用 Elvis 运算符在表达式中应用默认值。以下示例展示了如何在 @Value 表达式中使用 Elvis 运算符:

@Value("#{systemProperties['pop3.port'] ?: 25}")

This will inject a system property pop3.port if it is defined or 25 if not.
这将注入一个系统属性 pop3.port ,如果已定义,否则为 25。

(#expressions-operator-safe-navigation)4.3.15. Safe Navigation Operator

4.3.15. 安全导航操作符

The safe navigation operator is used to avoid a NullPointerException and comes from the Groovy language. Typically, when you have a reference to an object, you might need to verify that it is not null before accessing methods or properties of the object. To avoid this, the safe navigation operator returns null instead of throwing an exception.
安全导航操作符用于避免 NullPointerException ,并源自 Groovy 语言。通常,当你有一个对象的引用时,你可能需要在访问对象的方法或属性之前验证它不是 null。为了避免这种情况,安全导航操作符返回 null 而不是抛出异常。
The following example shows how to use the safe navigation operator:
以下示例展示了如何使用安全导航运算符:

ExpressionParser 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!!!
val parser = SpelExpressionParser() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val tesla = Inventor("Nikola Tesla", "Serbian") tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan")) var city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java) println(city) // Smiljan tesla.setPlaceOfBirth(null) city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java) println(city) // null - does not throw NullPointerException!!!

(#expressions-collection-selection)4.3.16. Collection Selection

4.3.16. 收集选择

Selection is a powerful expression language feature that lets you transform a source collection into another collection by selecting from its entries.
选择是一个强大的表达式语言特性,允许您通过从其条目中选择来将源集合转换成另一个集合。

Selection uses a syntax of .?[selectionExpression]. It filters the collection and returns a new collection that contains a subset of the original elements. For example, selection lets us easily get a list of Serbian inventors, as the following example shows:
选择使用语法 .?[selectionExpression] 。它过滤集合并返回一个包含原始元素子集的新集合。例如,选择使我们能够轻松地获取塞尔维亚发明家的列表,如下例所示:

List<Inventor> list = (List<Inventor>) parser.parseExpression( "members.?[nationality == 'Serbian']").getValue(societyContext);
val list = parser.parseExpression( "members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>

Selection is supported for arrays and anything that implements java.lang.Iterable or java.util.Map. For a list or array, the selection criteria is evaluated against each individual element. Against a map, the selection criteria is evaluated against each map entry (objects of the Java type Map.Entry). Each map entry has its key and value accessible as properties for use in the selection.
选择支持数组以及实现 java.lang.Iterablejava.util.Map 的任何内容。对于列表或数组,选择标准将针对每个单独的元素进行评估。对于映射,选择标准将针对每个映射条目(Java 类型的 Map.Entry 对象)进行评估。每个映射条目都可以将其 keyvalue 作为属性访问,用于选择。

The following expression returns a new map that consists of those elements of the original map where the entry’s value is less than 27:
以下表达式返回一个新的映射,该映射包含原始映射中值小于 27 的条目:

Map newMap = parser.parseExpression("map.?[value<27]").getValue();
val newMap = parser.parseExpression("map.?[value<27]").getValue()

In addition to returning all the selected elements, you can retrieve only the first or the last element. To obtain the first element matching the selection, the syntax is .^[selectionExpression]. To obtain the last matching selection, the syntax is .$[selectionExpression].
除了返回所有选定的元素外,您还可以仅检索第一个或最后一个元素。要获取与选择匹配的第一个元素,语法是 .^[selectionExpression] 。要获取与选择匹配的最后一个元素,语法是 .$[selectionExpression]

(#expressions-collection-projection)4.3.17. Collection Projection

4.3.17. 收集投影

Projection lets a collection drive the evaluation of a sub-expression, and the result is a new collection. The syntax for projection is .![projectionExpression]. For example, suppose we have a list of inventors but want the list of cities where they were born. Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor list. The following example uses projection to do so:
投影允许一个集合驱动子表达式的评估,结果是一个新的集合。投影的语法是 .![projectionExpression] 。例如,假设我们有一个发明家列表,但想要出生城市的列表。实际上,我们想要评估发明家列表中每个条目的'placeOfBirth.city' 。以下示例使用投影来实现这一点:

// returns ['Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
// returns ['Smiljan', 'Idvor' ] val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>

Projection is supported for arrays and anything that implements java.lang.Iterable or java.util.Map. When using a map to drive projection, the projection expression is evaluated against each entry in the map (represented as a Java Map.Entry). The result of a projection across a map is a list that consists of the evaluation of the projection expression against each map entry.
投影支持数组以及实现了 java.lang.Iterablejava.util.Map 的任何内容。当使用映射驱动投影时,投影表达式将对映射中的每个条目(表示为 Java Map.Entry )进行评估。映射上投影的结果是一个列表,它由对映射中每个条目进行投影表达式评估的结果组成。

(#expressions-templating)4.3.18. Expression templating

4.3.18. 表达式模板

Expression templates allow mixing literal text with one or more evaluation blocks. Each evaluation block is delimited with prefix and suffix characters that you can define. A common choice is to use #{ } as the delimiters, as the following example shows:
表达式模板允许将文本字面量与一个或多个评估块混合。每个评估块由您定义的前缀和后缀字符分隔。一个常见的选项是使用 #{ } 作为分隔符,如下例所示:

String randomPhrase = parser.parseExpression( "random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class); // evaluates to "random number is 0.7038186818312008"
val randomPhrase = parser.parseExpression( "random number is #{T(java.lang.Math).random()}", TemplateParserContext()).getValue(String::class.java) // evaluates to "random number is 0.7038186818312008"

The string is evaluated by concatenating the literal text 'random number is ' with the result of evaluating the expression inside the #{ } delimiter (in this case, the result of calling that random() method). The second argument to the parseExpression() method is of the type ParserContext. The ParserContext interface is used to influence how the expression is parsed in order to support the expression templating functionality. The definition of TemplateParserContext follows:
字符串通过连接字面文本 'random number is ' 和在 #{ } 分隔符内评估的表达式的结果来评估(在这种情况下,调用该 random() 方法的返回结果)。 parseExpression() 方法的第二个参数是 ParserContext 类型。使用 ParserContext 接口来影响表达式如何被解析,以支持表达式模板功能。 TemplateParserContext 的定义如下:

public class TemplateParserContext implements ParserContext { public String getExpressionPrefix() { return "#{"; } public String getExpressionSuffix() { return "}"; } public boolean isTemplate() { return true; } }
class TemplateParserContext : ParserContext { override fun getExpressionPrefix(): String { return "#{" } override fun getExpressionSuffix(): String { return "}" } override fun isTemplate(): Boolean { return true } }

(#expressions-example-classes)4.4. Classes Used in the Examples

4.4. 示例中使用的类

This section lists the classes used in the examples throughout this chapter.
本节列出了本章示例中使用的类。

package org.spring.samples.spel.inventor; import java.util.Date; import java.util.GregorianCalendar; public class Inventor { private String name; private String nationality; private String inventions; private Date birthdate; private PlaceOfBirth placeOfBirth; public Inventor(String name, String nationality) { GregorianCalendar c= new GregorianCalendar(); this.name = name; this.nationality = nationality; this.birthdate = c.getTime(); } public Inventor(String name, Date birthdate, String nationality) { this.name = name; this.nationality = nationality; this.birthdate = birthdate; } public Inventor() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNationality() { return nationality; } public void setNationality(String nationality) { this.nationality = nationality; } public Date getBirthdate() { return birthdate; } public void setBirthdate(Date birthdate) { this.birthdate = birthdate; } public PlaceOfBirth getPlaceOfBirth() { return placeOfBirth; } public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) { this.placeOfBirth = placeOfBirth; } public void setInventions(String inventions) { this.inventions = inventions; } public String getInventions() { return inventions; } }
class Inventor( var name: String, var nationality: String, var inventions: Array<String>? = null, var birthdate: Date = GregorianCalendar().time, var placeOfBirth: PlaceOfBirth? = null)

PlaceOfBirth.java

PlaceOfBirth.kt

出生地.java 出生地.kt

package org.spring.samples.spel.inventor; public class PlaceOfBirth { private String city; private String country; public PlaceOfBirth(String city) { this.city=city; } public PlaceOfBirth(String city, String country) { this(city); this.country = country; } public String getCity() { return city; } public void setCity(String s) { this.city = s; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
class PlaceOfBirth(var city: String, var country: String? = null) {

Society.java

Society.kt

Society.java Society.kt

package org.spring.samples.spel.inventor; import java.util.*; public class Society { private String name; public static String Advisors = "advisors"; public static String President = "president"; private List<Inventor> members = new ArrayList<Inventor>(); private Map officers = new HashMap(); public List getMembers() { return members; } public Map getOfficers() { return officers; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isMember(String name) { for (Inventor inventor : members) { if (inventor.getName().equals(name)) { return true; } } return false; } }
package org.spring.samples.spel.inventor import java.util.* class Society { val Advisors = "advisors" val President = "president" var name: String? = null val members = ArrayList<Inventor>() val officers = mapOf<Any, Any>() fun isMember(name: String): Boolean { for (inventor in members) { if (inventor.name == name) { return true } } return false } }
ON THIS PAGE