函数

您可以通过使用#functionName(…​)语法在表达式中调用用户定义的函数来扩展SpEL。函数可以通过setVariable()方法在EvaluationContext实现中注册为变量。

StandardEvaluationContext还定义了registerFunction(…​)方法,提供了一种方便的方式将函数注册为java.lang.reflect.Methodjava.lang.invoke.MethodHandle

由于函数与评估上下文中的变量共享一个公共命名空间,必须确保函数名称和变量名称不重叠。

以下示例展示了如何注册一个通过反射调用的用户定义函数,使用java.lang.reflect.Method

  • Java

  • Kotlin

Method method = ...;

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

val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method)

例如,考虑以下实用方法,用于反转字符串:

  • Java

  • Kotlin

public abstract class StringUtils {

	public static String reverseString(String input) {
		return new StringBuilder(input).reverse().toString();
	}
}
fun reverseString(input: String): String {
	return StringBuilder(input).reverse().toString()
}

您可以注册和使用上述方法,如以下示例所示:

  • Java

  • Kotlin

ExpressionParser parser = new SpelExpressionParser();

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
		StringUtils.class.getMethod("reverseString", String.class));

// 评估为 "olleh"
String helloWorldReversed = parser.parseExpression(
		"#reverseString('hello')").getValue(context, String.class);
val parser = SpelExpressionParser()

val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString.javaMethod)

// 评估为 "olleh"
val helloWorldReversed = parser.parseExpression(
		"#reverseString('hello')").getValue(context, String::class.java)

函数也可以注册为java.lang.invoke.MethodHandle。如果MethodHandle目标和参数在注册之前已完全绑定,这将实现潜在更高效的用例;但也支持部分绑定的句柄。

考虑String#formatted(String, Object…​)实例方法,根据模板和可变数量的参数生成消息。

您可以注册并使用formatted方法作为MethodHandle,如以下示例所示:

  • Java

  • Kotlin

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
		MethodType.methodType(String.class, Object[].class));
context.setVariable("message", mh);

// 评估为 "Simple message: <Hello World>"
String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
		.getValue(context, String.class);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
		MethodType.methodType(String::class.java, Array<Any>::class.java))
context.setVariable("message", mh)

// 评估为 "Simple message: <Hello World>"
val message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
		.getValue(context, String::class.java)

如上所示,绑定MethodHandle并注册绑定的MethodHandle也受支持。如果目标和所有参数都已绑定,这可能更有效。在这种情况下,在SpEL表达式中不需要参数,如以下示例所示:

  • Java

  • Kotlin

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

String template = "This is a %s message with %s words: <%s>";
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
		MethodType.methodType(String.class, Object[].class))
		.bindTo(template)
		.bindTo(varargs); //这里我们必须提供单个数组绑定的参数
context.setVariable("message", mh);

// 评估为 "This is a prerecorded message with 3 words: <Oh Hello World!>"
String message = parser.parseExpression("#message()")
		.getValue(context, String.class);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

val template = "This is a %s message with %s words: <%s>"
val varargs = arrayOf("prerecorded", 3, "Oh Hello World!", "ignored")

val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
		MethodType.methodType(String::class.java, Array<Any>::class.java))
		.bindTo(template)
		.bindTo(varargs) //这里我们必须提供单个数组绑定的参数
context.setVariable("message", mh)

// 评估为 "This is a prerecorded message with 3 words: <Oh Hello World!>"
val message = parser.parseExpression("#message()")
		.getValue(context, String::class.java)