Functions
可以通过使用 #functionName(…)
语法注册可在表达式内部调用的用户自定义函数,从而扩展 SpEL。可以使用 setVariable()
方法将函数作为变量注册到 EvaluationContext
实现中。
You can extend SpEL by registering user-defined functions that can be called within
expressions by using the #functionName(…)
syntax. Functions can be registered as
variables in EvaluationContext
implementations via the setVariable()
method.
|
由于函数与评估上下文中的变量共享一个公共名称空间,因此必须注意确保函数名和变量名不重叠。
Since functions share a common namespace with variables in the evaluation context, care must be taken to ensure that function names and variable names do not overlap.
以下示例演示如何使用 java.lang.reflect.Method
通过反射注册一个要调用的用户自定义函数:
The following example shows how to register a user-defined function to be invoked via
reflection using a 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)
例如,考虑以下用于反转字符串的实用程序方法:
For example, consider the following utility method that reverses a string:
-
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()
}
你可以注册并使用前面的方法,如下面的例子所示:
You can register and use the preceding method, as the following example shows:
-
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`的目标和参数在注册之前完全绑定,这会启用潜在的高效用例;然而,部分绑定的句柄也受支持。
A function can also be registered as a java.lang.invoke.MethodHandle
. This enables
potentially more efficient use cases if the MethodHandle
target and parameters have
been fully bound prior to registration; however, partially bound handles are also
supported.
考虑`String#formatted(String, Object…)`实例方法,它根据一个模板和数量可变的参数生成一条消息。
Consider the String#formatted(String, Object…)
instance method, which produces a
message according to a template and a variable number of arguments.
你可以注册并使用`formatted`方法作为`MethodHandle`,如下面的例子所示:
You can register and use the formatted
method as a MethodHandle
, as the following
example shows:
-
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 表达式中不需要提供参数,如下面的例子所示:
As hinted above, binding a MethodHandle
and registering the bound MethodHandle
is also
supported. This is likely to be more performant if both the target and all the arguments
are bound. In that case no arguments are necessary in the SpEL expression, as the
following example shows:
-
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); //here we have to provide arguments in a single array binding
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)
.bindTo(varargs) //here we have to provide arguments in a single array binding
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)