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 3bcbb6d83e..03a543a1ae 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 @@ -17,6 +17,7 @@ package org.springframework.expression.spel.support; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -38,15 +39,26 @@ import org.springframework.lang.Nullable; /** * A basic implementation of {@link EvaluationContext} that focuses on a subset - * of essential SpEL features and configuration options, and relies on default - * strategies otherwise. + * of essential SpEL features and configuration options. * - *
In many cases, the full extent of the SpEL is not + *
In many cases, the full extent of the SpEL language is not * required and should be meaningfully restricted. Examples include but are not * limited to data binding expressions, property-based filters, and others. To - * that effect, {@code SimpleEvaluationContext} supports only a subset of the - * SpEL language syntax that excludes references to Java types, constructors, - * and bean references. + * that effect, {@code SimpleEvaluationContext} is tailored to support only a + * subset of the SpEL language syntax, e.g. excluding references to Java types, + * constructors, and bean references. + * + *
When creating {@code SimpleEvaluationContext} you need to choose the level + * of support you need to deal with properties and methods in SpEL expressions. + * By default, {@link SimpleEvaluationContext#create()} enables only read access + * to properties via {@link DataBindingPropertyAccessor}. Alternatively, use + * {@link SimpleEvaluationContext#builder()} to configure the exact level of + * support needed, targeting one of, or some combination of the following: + *
Note that {@code SimpleEvaluationContext} cannot be configured with a
* default root object. Instead it is meant to be created once and used
@@ -56,6 +68,8 @@ import org.springframework.lang.Nullable;
*
* @author Rossen Stoyanchev
* @since 4.3.15
+ * @see StandardEvaluationContext
+ * @see DataBindingPropertyAccessor
*/
public class SimpleEvaluationContext implements EvaluationContext {
@@ -66,11 +80,9 @@ public class SimpleEvaluationContext implements EvaluationContext {
private final List Effectively, a shortcut for:
+ * Effectively, a shortcut for
+ * {@code propertyAccessor(new DataBindingPropertyAccessor(boolean))}.
+ * @param readOnlyAccess whether to read-only access to properties,
+ * {@code "true"}, or read and write, {@code "false"}.
+ */
+ public Builder dataBindingPropertyAccessor(boolean readOnlyAccess) {
+ return propertyAccessor(readOnlyAccess ?
+ DataBindingPropertyAccessor.forReadOnlyAccess() :
+ DataBindingPropertyAccessor.forReadWriteAccess());
+ }
+
+ /**
+ * Register a custom accessor for properties in expressions.
+ * By default, the builder does not enable property access.
+ */
+ public Builder propertyAccessor(PropertyAccessor... accessors) {
+ this.propertyAccessors.addAll(Arrays.asList(accessors));
+ return this;
+ }
+
+ /**
+ * Register a custom {@link TypeConverter}.
+ * By default {@link StandardTypeConverter} is used.
+ */
+ public Builder typeConverter(TypeConverter converter) {
+ this.typeConverter = converter;
+ return this;
+ }
+
+ public SimpleEvaluationContext build() {
+ return new SimpleEvaluationContext(this.propertyAccessors, this.typeConverter);
+ }
+ }
+
}
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 421519627e..abfd18313c 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
@@ -47,6 +47,7 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
+ * @see SimpleEvaluationContext
* @see ReflectivePropertyAccessor
* @see ReflectiveConstructorResolver
* @see ReflectiveMethodResolver
diff --git a/src/docs/asciidoc/core/core-expressions.adoc b/src/docs/asciidoc/core/core-expressions.adoc
index 9c8b125e22..8ae51a9688 100644
--- a/src/docs/asciidoc/core/core-expressions.adoc
+++ b/src/docs/asciidoc/core/core-expressions.adoc
@@ -181,23 +181,30 @@ create a boolean condition:
=== `EvaluationContext`
The interface `EvaluationContext` is used when evaluating an expression to resolve
-properties, methods, fields, and to help perform type conversion. The out-of-the-box
-implementations, `SimpleEvalutationContext` and `StandardEvaluationContext`, use
-reflection to manipulate the object, caching `java.lang.reflect.Method`,
-`java.lang.reflect.Field`, and `java.lang.reflect.Constructor` instances for increased
-performance.
+properties, methods, fields, and to help perform type conversion. There are two
+out-of-the-box implementations.
-`SimpleEvaluationContext` exposes a subset of essential SpEL language features and
-configuration options. Certain categories of expressions, do not require the full extent
-of the SpEL language syntax and arguably should be meaningfully restricted. Examples
+* `SimpleEvaluationContext` -- exposes a subset of essential SpEL language features and
+configuration options, for categories of expressions that do not require the full extent
+of the SpEL language syntax and should be meaningfully restricted. Examples
include but are not limited to data binding expressions, property-based filters, and
-others. To effect, `SimpleEvaluationContext` supports a subset of the SpEL language syntax
-that excludes references to Java types, constructors, and bean references.
+others.
-`StandardEvaluationContext` exposes the full set of SpEL language features and
+* `StandardEvaluationContext` -- exposes the full set of SpEL language features and
configuration options. You may use it to specify a default root object, and to configure
every available evaluation-related strategy.
+`SimpleEvaluationContext` is designed to support only a subset of the SpEL language syntax.
+It excludes Java type references, constructors, and bean references. It also requires
+explicit choosing the level of support for properties and methods in expressions.
+By default, the `create()` static factory method enables only read access to properties.
+You can also obtain a builder to configure the exact level of support needed, targeting
+one of, or some combination of the following:
+
+. Custom {@code PropertyAccessor} only (no reflection).
+. Data binding properties for read-only access.
+. Data binding properties for read and write.
+
[[expressions-type-conversion]]
==== Type conversion
@@ -225,7 +232,7 @@ being placed in it. A simple example:
Simple simple = new Simple();
simple.booleanList.add(true);
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext().create();
// false is passed in here as a string. SpEL and the conversion service will
// correctly recognize that it needs to be a Boolean and convert it
@@ -602,7 +609,7 @@ arrays and lists are obtained using square bracket notation.
[subs="verbatim,quotes"]
----
ExpressionParser parser = new SpelExpressionParser();
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
// Inventions Array
@@ -885,7 +892,7 @@ done within a call to `setValue` but can also be done inside a call to `getValue
[subs="verbatim,quotes"]
----
Inventor inventor = new Inventor();
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
parser.parseExpression("Name").setValue(context, inventor, "Alexander Seovic2");
@@ -953,7 +960,7 @@ are set using the method setVariable on `EvaluationContext` implementations.
[subs="verbatim,quotes"]
----
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("Name = #newName").getValue(context, tesla);
@@ -979,7 +986,7 @@ an expression are evaluated, #root always refers to the root.
// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
context.setVariable("primes",primes);
// all prime numbers > 10 from the list (using selection ?{...})
@@ -1001,7 +1008,7 @@ expression string. The function is registered through the `EvaluationContext`.
----
Method method = ...;
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
context.setVariable("myFunction", method);
----
@@ -1028,7 +1035,7 @@ The above method can then be registered and used as follows:
[subs="verbatim,quotes"]
----
ExpressionParser parser = new SpelExpressionParser();
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
context.setVariable("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", String.class));
@@ -1049,7 +1056,7 @@ lookup beans from an expression using the (@) symbol.
[subs="verbatim,quotes"]
----
ExpressionParser parser = new SpelExpressionParser();
- StandardEvaluationContext context = new StandardEvaluationContext();
+ StandardEvaluationContext context = StandardEvaluationContext.create();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
@@ -1062,7 +1069,7 @@ To access a factory bean itself, the bean name should instead be prefixed with a
[subs="verbatim,quotes"]
----
ExpressionParser parser = new SpelExpressionParser();
- StandardEvaluationContext context = new StandardEvaluationContext();
+ StandardEvaluationContext context = StandardEvaluationContext.create();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
@@ -1140,7 +1147,7 @@ Here is a more complex example.
ExpressionParser parser = new SpelExpressionParser();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class);
@@ -1172,7 +1179,7 @@ safe navigation operator will simply return null instead of throwing an exceptio
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
- SimpleEvaluationContext context = new SimpleEvaluationContext();
+ SimpleEvaluationContext context = SimpleEvaluationContext.create();
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // Smiljan
+ * SimpleEvaluationContext context = SimpleEvaluationContext.builder()
+ * .dataBindingPropertyAccessor(true)
+ * .build();
+ *
+ * @see #builder()
+ */
+ public static SimpleEvaluationContext create() {
+ return new Builder().dataBindingPropertyAccessor(true).build();
+ }
+
+ /**
+ * Return a builder to create a {@code SimpleEvaluationContext}.
+ * @see #create()
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+
+ /**
+ * Builder for {@code SimpleEvaluationContext}.
+ */
+ public static class Builder {
+
+ private final List