Operators

Spring 表达式语言支持以下类型的运算符:

The Spring Expression Language supports the following kinds of operators:

Relational Operators

可以使用标准运算符符号来支持关系运算符(相等、不等、小于、小于或等于、大于和大等)。这些运算符适用于 Number 类型以及实现 Comparable 的类型。以下清单显示了关系运算符的一些示例:

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. These operators work on Number types as well as types implementing Comparable. The following listing shows a few examples of relational operators:

  • Java

  • Kotlin

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

// uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);
// 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)

// uses CustomValue:::compareTo
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);

对于 null 的大于和小于比较遵循简单的规则:null 被视为没有内容(即不是零)。因此,任何其他值始终大于`null`(X > null 始终为 true),没有其他值小于没有内容(X < null 始终为 false)。

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 比较,而采用与零的比较(例如,X > 0X < 0)。

If you prefer numeric comparisons instead, avoid number-based null comparisons in favor of comparisons against zero (for example, X > 0 or X < 0).

每个符号运算符也可以指定为纯文本等效项。这避免了在表达式嵌入的文档类型中使用的符号具有特殊含义的问题(例如在 XML 文档中)。文本等价物如下:

Each symbolic operator can also be specified as a purely textual 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:

  • lt (<)

  • gt (>)

  • le (<=)

  • ge (>=)

  • eq (==)

  • ne (!=)

所有文本运算符不区分大小写。

All of the textual operators are case-insensitive.

除了标准关系运算符之外,SpEL 还支持 betweeninstanceof 和基于正则表达式的 matches 运算符。以下清单显示了所有这三个运算符的示例:

In addition to the standard relational operators, SpEL supports the between, instanceof, and regular expression-based matches operators. The following listing shows examples of all three:

  • Java

  • Kotlin

boolean result;

// evaluates to true
result = parser.parseExpression(
		"1 between {1, 5}").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"1 between {10, 15}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'xyz' instanceof T(Integer)").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
// evaluates to true
var result = parser.parseExpression(
		"1 between {1, 5}").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"1 between {10, 15}").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'xyz' instanceof T(Integer)").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

between 运算符的语法是 <input> between {<range_begin>, <range_end>},这实际上是 <input> >= <range_begin> && <input> ⇐ <range_end> 的快捷方式。

The syntax for the between operator is <input> between {<range_begin>, <range_end>}, which is effectively a shortcut for <input> >= <range_begin> && <input> ⇐ <range_end>}.

所以,1 between {1, 5} 评估为 true,而 1 between {5, 1} 评估为 false

Consequently, 1 between {1, 5} evaluates to true, while 1 between {5, 1} evaluates to false.

使用基本类型时要小心,因为它们会被立即装箱到其包装器类型中。例如,1 instanceof T(int) 评估为 false,而`1 instanceof T(Integer)` 评估为 true

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.

Logical Operators

SpEL 支持以下逻辑(布尔)运算符:

SpEL supports the following logical (boolean) operators:

  • and (&&)

  • or (||)

  • not (!)

所有文本运算符不区分大小写。

All of the textual operators are case-insensitive.

以下示例显示了如何使用逻辑运算符:

The following example shows how to use the logical operators:

  • Java

  • Kotlin

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

String Operators

你可以在字符串中使用以下运算符。

You can use the following operators on strings.

  • concatenation (+)

  • subtraction (-)

    • for use with a string containing a single character

  • repeat (*)

以下示例展示了 String 运算符的使用场景:

The following example shows the String operators in use:

  • Java

  • Kotlin

// -- Concatenation --

// evaluates to "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String.class);

// -- Character Subtraction --

// evaluates to 'a'
char ch = parser.parseExpression("'d' - 3")
		.getValue(char.class);

// -- Repeat --

// evaluates to "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
		.getValue(String.class);
// -- Concatenation --

// evaluates to "hello world"
val helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String::class.java)

// -- Character Subtraction --

// evaluates to 'a'
val ch = parser.parseExpression("'d' - 3")
		.getValue(Character::class.java);

// -- Repeat --

// evaluates to "abcabc"
val repeated = parser.parseExpression("'abc' * 2")
		.getValue(String::class.java);

Mathematical Operators

你可以在数字上使用以下运算符,系统会强制执行标准运算符优先级。

You can use the following operators on numbers, and standard operator precedence is enforced.

  • addition (+)

  • subtraction (-)

  • increment (++)

  • decrement (--)

  • multiplication (*)

  • division (/)

  • modulus (%)

  • exponential power (^)

除法和取模运算符也可以用纯粹的文本等价物来指定。这避免了嵌入表达式的文档类型中使用的符号具有特殊含义的问题(例如在 XML 文档中)。文本等价物如下:

The division and modulus operators can also be specified as a purely textual 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:

  • div (/)

  • mod (%)

所有文本运算符不区分大小写。

All of the textual operators are case-insensitive.

增量和减量运算符可以用前缀(A`、`--A`)或后缀(`AA--)表示法与可写入的变量或属性一起使用。

The increment and decrement operators can be used with either prefix (A`, `--A`) or postfix (`A, A--) notation with variables or properties that can be written to.

以下示例展示了数学运算符的使用场景:

The following example shows the mathematical operators in use:

  • Java

  • Kotlin

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// -- Addition --

int two = parser.parseExpression("1 + 1").getValue(int.class);  // 2

// -- Subtraction --

int four = parser.parseExpression("1 - -3").getValue(int.class);  // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class);  // -9000

// -- Increment --

// The counter property in Inventor has an initial value of 0.

// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);

// -- Decrement --

// The counter property in Inventor has a value of 2.

// evaluates to 6; counter is now 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);

// -- Multiplication --

six = parser.parseExpression("-2 * -3").getValue(int.class);  // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class);  // 24.0

// -- Division --

int minusTwo = parser.parseExpression("6 / -3").getValue(int.class);  // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class);  // 1.0

// -- Modulus --

int three = parser.parseExpression("7 % 4").getValue(int.class);  // 3

int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class);  // 1

// -- Exponential power --

int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class);  // Integer.MAX_VALUE

int minInt = parser.parseExpression("-2^31").getValue(int.class);  // Integer.MIN_VALUE

// -- Operator precedence --

int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class);  // -21
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()

// -- Addition --

var two = parser.parseExpression("1 + 1").getValue(Int::class.java)  // 2

// -- Subtraction --

val four = parser.parseExpression("1 - -3").getValue(Int::class.java)  // 4

val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class.java)  // -9000

// -- Increment --

// The counter property in Inventor has an initial value of 0.

// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, Int::class.java)

// evaluates to 5; counter is now 2
var five = parser.parseExpression("3 + ++counter").getValue(context, inventor, Int::class.java)

// -- Decrement --

// The counter property in Inventor has a value of 2.

// evaluates to 6; counter is now 1
var six = parser.parseExpression("counter-- + 4").getValue(context, inventor, Int::class.java)

// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, Int::class.java)

// -- Multiplication --

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 oneInt = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java)  // 1

// -- Exponential power --

val maxInt = parser.parseExpression("(2^31) - 1").getValue(Int::class.java)  // Integer.MAX_VALUE

val minInt = parser.parseExpression("-2^31").getValue(Int::class.java)  // Integer.MIN_VALUE

// -- Operator precedence --

val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java)  // -21

The Assignment Operator

要设置属性,请使用赋值运算符(=)。这通常在调用 setValue 时完成,但也可以在调用 getValue 时完成。以下列表展示了使用赋值运算符的两种方式:

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:

  • Java

  • Kotlin

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)

Overloaded Operators

默认情况下,SpEL 的 Operation 枚举(ADDSUBTRACTDIVIDEMULTIPLYMODULUSPOWER)中定义的数学运算支持简单类型(如数字)。通过提供 OperatorOverloader 的实现,表达式语言可以对其他类型支持这些运算。

By default, the mathematical operations defined in SpEL’s Operation enum (ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS, and POWER) support simple types like numbers. By providing an implementation of OperatorOverloader, the expression language can support these operations on other types.

例如,如果我们想重载 ADD 运算符以允许使用 + 号连接两个列表,我们可以按如下所示实现一个自定义 OperatorOverloader

For example, if we want to overload the ADD operator to allow two lists to be concatenated using the + sign, we can implement a custom OperatorOverloader as follows.

pubic class ListConcatenation implements OperatorOverloader {

	@Override
	public boolean overridesOperation(Operation operation, Object left, Object right) {
		return (operation == Operation.ADD &&
				left instanceof List && right instanceof List);
	}

	@Override
	public Object operate(Operation operation, Object left, Object right) {
		if (operation == Operation.ADD &&
				left instanceof List list1 && right instanceof List list2) {

			List result = new ArrayList(list1);
			result.addAll(list2);
			return result;
		}
		throw new UnsupportedOperationException(
			"No overload for operation %s and operands [%s] and [%s]"
				.formatted(operation, left, right));
	}
}

如果我们在 StandardEvaluationContext 中将 ListConcatenation 注册为 OperatorOverloader,那么我们就可以评估诸如 {1, 2, 3} + {4, 5} 的表达式,如下例所示。

If we register ListConcatenation as the OperatorOverloader in a StandardEvaluationContext, we can then evaluate expressions like {1, 2, 3} + {4, 5} as demonstrated in the following example.

  • Java

  • Kotlin

StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());

// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())

// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)

OperatorOverloader 不会更改运算符的默认语义。例如,在上述示例中,2 + 2 仍然等于 4

An OperatorOverloader does not change the default semantics for an operator. For example, 2 + 2 in the above example still evaluates to 4.

任何使用重载运算符的表达式都无法编译。有关详细信息,请参阅 Compiler Limitations

Any expression that uses an overloaded operator cannot be compiled. See Compiler Limitations for details.