函数
你可以通过注册用户自定义函数来扩展SpEL,这些函数可以使用#functionName(…)语法在表达式中被调用。与标准方法调用一样,函数调用也支持可变参数。
在EvaluationContext的实现中,可以通过setVariable()方法将函数注册为变量。
StandardEvaluationContext 还定义了 registerFunction(…) 方法,这些方法提供了一种方便的方式来将函数注册为 java.lang.reflect.Method 或 java.lang.invoke.MethodHandle。
由于函数在求值上下文中与变量共享相同的命名空间,因此必须小心确保函数名和变量名不会重叠。
以下示例展示了如何注册一个用户自定义函数,以便通过反射(reflection)使用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));
// evaluates to "olleh"
String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString.javaMethod)
// evaluates to "olleh"
val helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String::class.java)
函数也可以注册为java.lang.invoke.MethodHandle。如果在注册之前MethodHandle的目标和参数已经完全绑定,那么这将实现可能更高效的使用场景;不过,也支持部分绑定的MethodHandle。
考虑String#formatted(Object…)实例方法,该方法根据模板和可变数量的参数(varargs)生成一条消息。
你可以像下面的例子所示,将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);
// evaluates to "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)
// evaluates to "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)
// Here we have to provide the arguments in a single array binding:
.bindTo(varargs);
context.setVariable("message", mh);
// evaluates to "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)
// Here we have to provide the arguments in a single array binding:
.bindTo(varargs)
context.setVariable("message", mh)
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
val message = parser.parseExpression("#message()")
.getValue(context, String::class.java)