Functions
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. |
The following example shows how to register a user-defined function to be invoked via
reflection using a java.lang.reflect.Method
:
-
Java
Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
For example, consider the following utility method that reverses a string:
-
Java
public abstract class StringUtils {
public static String reverseString(String input) {
return new StringBuilder(input).reverse().toString();
}
}
You can register and use the preceding method, as the following example shows:
-
Java
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);
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.
Consider the String#formatted(String, Object…)
instance method, which produces a
message according to a template and a variable number of arguments.
You can register and use the formatted
method as a MethodHandle
, as the following
example shows:
-
Java
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);
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
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);