函數

您可以透過註冊使用者定義函數來擴展 SpEL,這些函數可以使用 #functionName(…​) 語法在運算式中呼叫。函數可以透過 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));

// 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 目標和參數在註冊之前已完全綁定,則這可以實現可能更有效率的使用案例;但是,也支援部分綁定的 Handle。

考慮 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);

// 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)
		.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)