此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

运算符

关系运算符

关系运算符(等于、不等于、小于、小于等于、大于和大于等于)支持使用标准运算符符号。 这些运算符适用于 Number 类型以及实现了 Comparable 接口的类型。 以下列表展示了一些关系运算符的示例:spring-doc.cadn.net.cn

// 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 被视为“无”(即不视为零)。因此,任何其他值总是大于 nullX > null 始终为 true),而没有任何值会小于“无”(X < null 始终为 false)。spring-doc.cadn.net.cn

如果你更倾向于使用数值比较,请避免基于数字的 null 比较,而应采用与零的比较(例如,X > 0X < 0)。spring-doc.cadn.net.cn

每个符号运算符也可以指定为纯文本形式的等效表示。这样可以避免在表达式所嵌入的文档类型(例如 XML 文档)中,所使用的符号具有特殊含义而引发的问题。这些文本等效形式包括:spring-doc.cadn.net.cn

所有文本操作符均不区分大小写。spring-doc.cadn.net.cn

除了标准的关系运算符外,SpEL 还支持 betweeninstanceof 以及基于正则表达式的 matches 运算符。以下示例展示了这三种运算符的用法:spring-doc.cadn.net.cn

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> 的简写形式。spring-doc.cadn.net.cn

因此,1 between {1, 5} 的求值结果为 true,而 1 between {5, 1} 的求值结果为 falsespring-doc.cadn.net.cn

注意基本数据类型,因为它们会立即被装箱为其对应的包装类型。例如,1 instanceof T(int) 的求值结果为 false,而 1 instanceof T(Integer) 的求值结果为 true

逻辑运算符

SpEL 支持以下逻辑(boolean)运算符:spring-doc.cadn.net.cn

所有文本操作符均不区分大小写。spring-doc.cadn.net.cn

以下示例展示了如何使用逻辑运算符:spring-doc.cadn.net.cn

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

字符串运算符

你可以在字符串上使用以下运算符。spring-doc.cadn.net.cn

以下示例展示了 String 运算符的使用:spring-doc.cadn.net.cn

// -- 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(Char::class.java)

// -- Repeat --

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

数学运算符

你可以对数字使用以下运算符,并且会遵循标准的运算符优先级规则。spring-doc.cadn.net.cn

除法和取模运算符也可以使用纯文本形式的等效表示。 这样可以避免在表达式所嵌入的文档类型中(例如 XML 文档),这些符号具有特殊含义而引发的问题。其文本等效形式为:spring-doc.cadn.net.cn

所有文本操作符均不区分大小写。spring-doc.cadn.net.cn

递增和递减运算符可以与可写入的变量或属性一起使用,支持前缀(++A--A)或后缀(A++A--)表示法。spring-doc.cadn.net.cn

以下示例展示了数学运算符的使用:spring-doc.cadn.net.cn

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

赋值运算符

要设置一个属性,请使用赋值运算符(=)。这通常在调用 setValue 时完成,但也可以在调用 getValue 时进行。以下代码示例展示了使用赋值运算符的两种方式:spring-doc.cadn.net.cn

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)

重载运算符

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

例如,如果我们希望重载 ADD 运算符,以允许使用 + 号连接两个列表,可以按如下方式实现一个自定义的 OperatorOverloaderspring-doc.cadn.net.cn

import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;

import java.util.ArrayList;
import java.util.List;

public 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));
	}

}
import org.springframework.expression.Operation
import org.springframework.expression.OperatorOverloader

class ListConcatenation: OperatorOverloader {

	override fun overridesOperation(operation: Operation, left: Any?, right: Any?): Boolean {
		return operation == Operation.ADD && left is List<*> && right is List<*>
	}

	override fun operate(operation: Operation, left: Any?, right: Any?): Any {
		if (operation == Operation.ADD && left is List<*> && right is List<*>) {
			return left + right
		}

		throw UnsupportedOperationException(
			"No overload for operation $operation and operands [$left] and [$right]")
	}

}

如果我们将 ListConcatenation 注册为 OperatorOverloader 中的 StandardEvaluationContext,那么我们就可以对诸如 {1, 2, 3} + {4, 5} 这样的表达式进行求值,如下例所示。spring-doc.cadn.net.cn

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);
val context = StandardEvaluationContext()
context.operatorOverloader = 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 的计算结果仍然是 4spring-doc.cadn.net.cn

任何使用重载运算符的表达式都无法编译。详情请参见 编译器限制spring-doc.cadn.net.cn