diff --git a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/functions.adoc b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/functions.adoc index 79a39bb0386..6e1e2bf81f6 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/functions.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/functions.adoc @@ -1,10 +1,26 @@ [[expressions-ref-functions]] = Functions -You can extend SpEL by registering user-defined functions that can be called within the -expression string. The function is registered through the `EvaluationContext`. The -following example shows how to register a user-defined function to be invoked via reflection -(i.e. a `Method`): +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. + +[TIP] +==== +`StandardEvaluationContext` also defines `registerFunction(...)` methods that provide a +convenient way to register a function as a `java.lang.reflect.Method` or a +`java.lang.invoke.MethodHandle`. +==== + +[WARNING] +==== +Since functions share a common namespace with +xref:core/expressions/language-ref/variables.adoc[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`: [tabs] ====== diff --git a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc index 3516cac84ed..ac2fce743a2 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc @@ -20,6 +20,13 @@ characters. * dollar sign: `$` ==== +[WARNING] +==== +Since variables share a common namespace with +xref:core/expressions/language-ref/functions.adoc[functions] in the evaluation context, +care must be taken to ensure that variable names and functions names do not overlap. +==== + The following example shows how to use variables. [tabs] diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java index 91f12190ef6..f8cd8a1cce5 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java @@ -74,6 +74,14 @@ import org.springframework.lang.Nullable; * {@code EvaluationContext} and a root object as arguments: * {@link org.springframework.expression.Expression#getValue(EvaluationContext, Object)}. * + *

In addition to support for setting and looking up variables as defined in + * the {@link EvaluationContext} API, {@code SimpleEvaluationContext} also + * provides support for {@linkplain #setVariable(String, Object) registering} and + * {@linkplain #lookupVariable(String) looking up} functions as variables. Since + * functions share a common namespace with the variables in this evaluation + * context, care must be taken to ensure that function names and variable names + * do not overlap. + * *

For more power and flexibility, in particular for internal configuration * scenarios, consider using {@link StandardEvaluationContext} instead. * @@ -214,11 +222,31 @@ public final class SimpleEvaluationContext implements EvaluationContext { throw new SpelEvaluationException(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED, "#" + name); } + /** + * Set a named variable or function in this evaluation context to the specified + * value. + *

A function can be registered as a {@link java.lang.reflect.Method} or + * a {@link java.lang.invoke.MethodHandle}. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain SimpleEvaluationContext + * class-level documentation} for details. + * @param name the name of the variable or function to set + * @param value the value to be placed in the variable or function + * @see #lookupVariable(String) + */ @Override public void setVariable(String name, @Nullable Object value) { this.variables.put(name, value); } + /** + * Look up a named variable or function within this evaluation context. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain SimpleEvaluationContext + * class-level documentation} for details. + * @param name the name of the variable or function to look up + * @return the value of the variable or function, or {@code null} if not found + */ @Override @Nullable public Object lookupVariable(String name) { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java index 7fa3e515c3c..5df60b3bd0e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java @@ -48,6 +48,16 @@ import org.springframework.util.Assert; * to reliably locate user types. See {@link #setTypeLocator(TypeLocator)} for * details. * + *

In addition to support for setting and looking up variables as defined in + * the {@link EvaluationContext} API, {@code StandardEvaluationContext} also + * provides support for registering and looking up functions. The + * {@code registerFunction(...)} methods provide a convenient way to register a + * function as a {@link Method} or a {@link MethodHandle}; however, a function + * can also be registered via {@link #setVariable(String, Object)} or + * {@link #setVariables(Map)}. Since functions share a namespace with the variables + * in this evaluation context, care must be taken to ensure that function names + * and variable names do not overlap. + * *

For a simpler, builder-style context variant for data-binding purposes, * consider using {@link SimpleEvaluationContext} instead which allows for * opting into several SpEL features as needed by specific use cases. @@ -253,6 +263,25 @@ public class StandardEvaluationContext implements EvaluationContext { return this.operatorOverloader; } + /** + * Set a named variable in this evaluation context to a specified value. + *

If the specified {@code name} is {@code null}, it will be ignored. If + * the specified {@code value} is {@code null}, the named variable will be + * removed from this evaluation context. + *

In contrast to {@link #assignVariable(String,java.util.function.Supplier)}, + * this method should only be invoked programmatically when interacting directly + * with the {@code EvaluationContext} — for example, to provide initial + * configuration for the context. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain StandardEvaluationContext + * class-level documentation} for details. + * @param name the name of the variable to set + * @param value the value to be placed in the variable + * @see #setVariables(Map) + * @see #registerFunction(String, Method) + * @see #registerFunction(String, MethodHandle) + * @see #lookupVariable(String) + */ @Override public void setVariable(@Nullable String name, @Nullable Object value) { // For backwards compatibility, we ignore null names here... @@ -271,6 +300,9 @@ public class StandardEvaluationContext implements EvaluationContext { /** * Set multiple named variables in this evaluation context to the specified values. *

This is a convenience variant of {@link #setVariable(String, Object)}. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain StandardEvaluationContext + * class-level documentation} for details. * @param variables the names and values of the variables to set * @see #setVariable(String, Object) */ @@ -280,9 +312,9 @@ public class StandardEvaluationContext implements EvaluationContext { /** * Register the specified {@link Method} as a SpEL function. - *

Note: Function names share a namespace with the variables in this - * evaluation context, as populated by {@link #setVariable(String, Object)}. - * Make sure that specified function names and variable names do not overlap. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain StandardEvaluationContext + * class-level documentation} for details. * @param name the name of the function * @param method the {@code Method} to register * @see #registerFunction(String, MethodHandle) @@ -293,9 +325,9 @@ public class StandardEvaluationContext implements EvaluationContext { /** * Register the specified {@link MethodHandle} as a SpEL function. - *

Note: Function names share a namespace with the variables in this - * evaluation context, as populated by {@link #setVariable(String, Object)}. - * Make sure that specified function names and variable names do not overlap. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain StandardEvaluationContext + * class-level documentation} for details. * @param name the name of the function * @param methodHandle the {@link MethodHandle} to register * @since 6.1 @@ -305,6 +337,14 @@ public class StandardEvaluationContext implements EvaluationContext { this.variables.put(name, methodHandle); } + /** + * Look up a named variable or function within this evaluation context. + *

Note that variables and functions share a common namespace in this + * evaluation context. See the {@linkplain StandardEvaluationContext + * class-level documentation} for details. + * @param name the name of the variable or function to look up + * @return the value of the variable or function, or {@code null} if not found + */ @Override @Nullable public Object lookupVariable(String name) { @@ -333,9 +373,9 @@ public class StandardEvaluationContext implements EvaluationContext { /** * Apply the internal delegates of this instance to the specified * {@code evaluationContext}. Typically invoked right after the new context - * instance has been created to reuse the delegates. Do not modify the + * instance has been created to reuse the delegates. Does not modify the * {@linkplain #setRootObject(Object) root object} or any registered - * {@linkplain #setVariables(Map) variables}. + * {@linkplain #setVariable variables or functions}. * @param evaluationContext the evaluation context to update * @since 6.1.1 */