运算符

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

关系运算符

关系运算符(等于,不等于,小于,小于等于,大于,大于等于)支持使用标准运算符表示法。这些运算符适用于Number类型以及实现Comparable接口的类型。以下示例展示了一些关系运算符的用法:

  • Java

  • Kotlin

// 结果为true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// 结果为false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// 结果为true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

// 使用CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);
// 结果为true
val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java)

// 结果为false
val falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean::class.java)

// 结果为true
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)

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

大于和小于与null的比较遵循一个简单的规则:null被视为不存在(而不是零)。因此,任何其他值始终大于nullX > null始终为true),而没有其他值会小于不存在(X < null始终为false)。

如果您更喜欢数值比较,请避免基于数字的null比较,而是使用与零的比较(例如,X > 0X < 0)。

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

  • lt<

  • gt>

  • le<=

  • ge>=

  • eq==

  • ne!=

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

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

  • Java

  • Kotlin

boolean result;

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

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

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

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

// 结果为true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean.class);

// 结果为false
result = parser.parseExpression(
		"'xyz' instanceof T(Integer)").getValue(Boolean.class);

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

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

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

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

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

// 结果为true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean::class.java)

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

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

// 结果为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>}的快捷方式。

因此,1 between {1, 5}的结果为true,而1 between {5, 1}的结果为false

对于基本类型要小心,因为它们会立即装箱为其包装类型。例如,1 instanceof T(int)的结果为false,而1 instanceof T(Integer)的结果为true

逻辑运算符

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

  • and&&

  • or||

  • not!

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

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

  • Java

  • Kotlin

// -- AND --

// 结果为false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// 结果为true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// 结果为true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// 结果为true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// 结果为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 --

// 结果为false
val falseValue = parser.parseExpression("true and false").getValue(Boolean::class.java)

// 结果为true
val expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- OR --

// 结果为true
val trueValue = parser.parseExpression("true or false").getValue(Boolean::class.java)

// 结果为true
val expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- NOT --

// 结果为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运算符的使用:

  • Java

  • Kotlin

// -- 连接 --

// 结果为 "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String.class);

// -- 字符减法 --

// 结果为 'a'
char ch = parser.parseExpression("'d' - 3")
		.getValue(char.class);

// -- 重复 --

// 结果为 "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
		.getValue(String.class);
// -- 连接 --

// 结果为 "hello world"
val helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String::class.java)

// -- 字符减法 --

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

// -- 重复 --

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

数学运算符

您可以对数字使用以下运算符,并且强制执行标准运算符优先级。

  • 加法(+

  • 减法(-

  • 递增(++

  • 递减(--

  • 乘法(*

  • 除法(/

  • 取模(%

  • 指数幂(^

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

  • div/

  • mod%

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

递增和递减运算符可用于具有可写属性或变量的前缀(++A--A)或后缀(A++A--)表示法。

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

  • Java

  • Kotlin

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

// -- 加法 --

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

// -- 减法 --

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

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

// -- 递增 --

// Inventor中的counter属性初始值为0。

// 计算结果为2;counter现在为1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);

// 计算结果为5;counter现在为2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);

// -- 递减 --

// Inventor中的counter属性值为2。

// 计算结果为6;counter现在为1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);

// 计算结果为5;counter现在为0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);

// -- 乘法 --

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

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

// -- 除法 --

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

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

// -- 取模 --

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

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

// -- 指数幂 --

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

// -- 运算符优先级 --

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

// -- 加法 --

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

// -- 减法 --

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

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

// -- 递增 --

// Inventor中的counter属性初始值为0。

// 计算结果为2;counter现在为1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, Int::class.java)

// 计算结果为5;counter现在为2
var five = parser.parseExpression("3 + ++counter").getValue(context, inventor, Int::class.java)

// -- 递减 --

// Inventor中的counter属性值为2。

// 计算结果为6;counter现在为1
var six = parser.parseExpression("counter-- + 4").getValue(context, inventor, Int::class.java)

// 计算结果为5;counter现在为0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, Int::class.java)

// -- 乘法 --

six = parser.parseExpression("-2 * -3").getValue(Int::class.java)  // 6

val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java)  // 24.0

// -- 除法 --

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

// -- 取模 --

val three = parser.parseExpression("7 % 4").getValue(Int::class.java)  // 3

val oneInt = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java)  // 1

// -- 指数幂 --

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

// -- 运算符优先级 --

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

赋值运算符

要设置属性,请使用赋值运算符(=)。通常在调用setValue时执行此操作,但也可以在调用getValue时执行。以下清单展示了使用赋值运算符的两种方式:

  • Java

  • Kotlin

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

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");

// 或者
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")

// 或者
val aleks = parser.parseExpression(
		"name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)

操作符重载

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

例如,如果我们想要重载ADD操作符,允许使用+符号连接两个列表,我们可以实现一个自定义的OperatorOverloader如下。

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}

  • Java

  • Kotlin

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

// 评估为新列表:[1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())

// 评估为新列表:[1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)

OperatorOverloader不会改变操作符的默认语义。例如,在上面的示例中,2 + 2仍然评估为4

任何使用重载操作符的表达式都无法编译。有关详细信息,请参阅编译器限制