parent
b5511645b8
commit
19293b9847
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.expression.spel.support;
|
package org.springframework.expression.spel.support;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -38,15 +39,26 @@ import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of {@link EvaluationContext} that focuses on a subset
|
* A basic implementation of {@link EvaluationContext} that focuses on a subset
|
||||||
* of essential SpEL features and configuration options, and relies on default
|
* of essential SpEL features and configuration options.
|
||||||
* strategies otherwise.
|
|
||||||
*
|
*
|
||||||
* <p>In many cases, the full extent of the SpEL is not
|
* <p>In many cases, the full extent of the SpEL language is not
|
||||||
* required and should be meaningfully restricted. Examples include but are not
|
* required and should be meaningfully restricted. Examples include but are not
|
||||||
* limited to data binding expressions, property-based filters, and others. To
|
* limited to data binding expressions, property-based filters, and others. To
|
||||||
* that effect, {@code SimpleEvaluationContext} supports only a subset of the
|
* that effect, {@code SimpleEvaluationContext} is tailored to support only a
|
||||||
* SpEL language syntax that excludes references to Java types, constructors,
|
* subset of the SpEL language syntax, e.g. excluding references to Java types,
|
||||||
* and bean references.
|
* constructors, and bean references.
|
||||||
|
*
|
||||||
|
* <p>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:
|
||||||
|
* <ul>
|
||||||
|
* <li>Custom {@code PropertyAccessor} only (no reflection).</li>
|
||||||
|
* <li>Data binding properties for read-only access.</li>
|
||||||
|
* <li>Data binding properties for read and write.</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Note that {@code SimpleEvaluationContext} cannot be configured with a
|
* <p>Note that {@code SimpleEvaluationContext} cannot be configured with a
|
||||||
* default root object. Instead it is meant to be created once and used
|
* 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
|
* @author Rossen Stoyanchev
|
||||||
* @since 4.3.15
|
* @since 4.3.15
|
||||||
|
* @see StandardEvaluationContext
|
||||||
|
* @see DataBindingPropertyAccessor
|
||||||
*/
|
*/
|
||||||
public class SimpleEvaluationContext implements EvaluationContext {
|
public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
|
@ -66,11 +80,9 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
private final List<PropertyAccessor> propertyAccessors;
|
private final List<PropertyAccessor> propertyAccessors;
|
||||||
|
|
||||||
private final List<ConstructorResolver> constructorResolvers =
|
private final List<ConstructorResolver> constructorResolvers = Collections.emptyList();
|
||||||
Collections.singletonList(new ReflectiveConstructorResolver());
|
|
||||||
|
|
||||||
private final List<MethodResolver> methodResolvers =
|
private final List<MethodResolver> methodResolvers = Collections.emptyList();
|
||||||
Collections.singletonList(new ReflectiveMethodResolver());
|
|
||||||
|
|
||||||
private final TypeConverter typeConverter;
|
private final TypeConverter typeConverter;
|
||||||
|
|
||||||
|
@ -81,25 +93,12 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
private final Map<String, Object> variables = new HashMap<>();
|
private final Map<String, Object> variables = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public SimpleEvaluationContext() {
|
private SimpleEvaluationContext(List<PropertyAccessor> accessors, @Nullable TypeConverter converter) {
|
||||||
this(null, null);
|
this.propertyAccessors = Collections.unmodifiableList(new ArrayList<>(accessors));
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleEvaluationContext(@Nullable List<PropertyAccessor> accessors, @Nullable TypeConverter converter) {
|
|
||||||
this.propertyAccessors = initPropertyAccessors(accessors);
|
|
||||||
this.typeConverter = converter != null ? converter : new StandardTypeConverter();
|
this.typeConverter = converter != null ? converter : new StandardTypeConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static List<PropertyAccessor> initPropertyAccessors(@Nullable List<PropertyAccessor> accessors) {
|
|
||||||
if (accessors == null) {
|
|
||||||
accessors = new ArrayList<>(5);
|
|
||||||
accessors.add(new ReflectivePropertyAccessor());
|
|
||||||
}
|
|
||||||
return accessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code SimpleEvaluationContext} cannot be configured with a root object.
|
* {@code SimpleEvaluationContext} cannot be configured with a root object.
|
||||||
* It is meant for repeated use with
|
* It is meant for repeated use with
|
||||||
|
@ -118,7 +117,8 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a single {@link ReflectiveConstructorResolver}.
|
* Return an empty list, always, since this context does not support the
|
||||||
|
* use of type references.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<ConstructorResolver> getConstructorResolvers() {
|
public List<ConstructorResolver> getConstructorResolvers() {
|
||||||
|
@ -190,4 +190,76 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
return this.variables.get(name);
|
return this.variables.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@code SimpleEvaluationContext} with read-only access to
|
||||||
|
* public properties via {@link DataBindingPropertyAccessor}.
|
||||||
|
* <p>Effectively, a shortcut for:
|
||||||
|
* <pre class="code">
|
||||||
|
* SimpleEvaluationContext context = SimpleEvaluationContext.builder()
|
||||||
|
* .dataBindingPropertyAccessor(true)
|
||||||
|
* .build();
|
||||||
|
* </pre>
|
||||||
|
* @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<PropertyAccessor> propertyAccessors = new ArrayList<>();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TypeConverter typeConverter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable access to public properties for data binding purposes.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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}.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.springframework.util.Assert;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see SimpleEvaluationContext
|
||||||
* @see ReflectivePropertyAccessor
|
* @see ReflectivePropertyAccessor
|
||||||
* @see ReflectiveConstructorResolver
|
* @see ReflectiveConstructorResolver
|
||||||
* @see ReflectiveMethodResolver
|
* @see ReflectiveMethodResolver
|
||||||
|
|
|
@ -181,23 +181,30 @@ create a boolean condition:
|
||||||
=== `EvaluationContext`
|
=== `EvaluationContext`
|
||||||
|
|
||||||
The interface `EvaluationContext` is used when evaluating an expression to resolve
|
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
|
properties, methods, fields, and to help perform type conversion. There are two
|
||||||
implementations, `SimpleEvalutationContext` and `StandardEvaluationContext`, use
|
out-of-the-box implementations.
|
||||||
reflection to manipulate the object, caching `java.lang.reflect.Method`,
|
|
||||||
`java.lang.reflect.Field`, and `java.lang.reflect.Constructor` instances for increased
|
|
||||||
performance.
|
|
||||||
|
|
||||||
`SimpleEvaluationContext` exposes a subset of essential SpEL language features and
|
* `SimpleEvaluationContext` -- exposes a subset of essential SpEL language features and
|
||||||
configuration options. Certain categories of expressions, do not require the full extent
|
configuration options, for categories of expressions that do not require the full extent
|
||||||
of the SpEL language syntax and arguably should be meaningfully restricted. Examples
|
of the SpEL language syntax and should be meaningfully restricted. Examples
|
||||||
include but are not limited to data binding expressions, property-based filters, and
|
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
|
others.
|
||||||
that excludes references to Java types, constructors, and bean references.
|
|
||||||
|
|
||||||
`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
|
configuration options. You may use it to specify a default root object, and to configure
|
||||||
every available evaluation-related strategy.
|
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]]
|
[[expressions-type-conversion]]
|
||||||
==== Type conversion
|
==== Type conversion
|
||||||
|
@ -225,7 +232,7 @@ being placed in it. A simple example:
|
||||||
Simple simple = new Simple();
|
Simple simple = new Simple();
|
||||||
simple.booleanList.add(true);
|
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
|
// 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
|
// 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"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
|
|
||||||
// Inventions Array
|
// 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"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
Inventor inventor = new Inventor();
|
Inventor inventor = new Inventor();
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
|
|
||||||
parser.parseExpression("Name").setValue(context, inventor, "Alexander Seovic2");
|
parser.parseExpression("Name").setValue(context, inventor, "Alexander Seovic2");
|
||||||
|
|
||||||
|
@ -953,7 +960,7 @@ are set using the method setVariable on `EvaluationContext` implementations.
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
context.setVariable("newName", "Mike Tesla");
|
context.setVariable("newName", "Mike Tesla");
|
||||||
|
|
||||||
parser.parseExpression("Name = #newName").getValue(context, 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
|
// create parser and set variable 'primes' as the array of integers
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
context.setVariable("primes",primes);
|
context.setVariable("primes",primes);
|
||||||
|
|
||||||
// all prime numbers > 10 from the list (using selection ?{...})
|
// all prime numbers > 10 from the list (using selection ?{...})
|
||||||
|
@ -1001,7 +1008,7 @@ expression string. The function is registered through the `EvaluationContext`.
|
||||||
----
|
----
|
||||||
Method method = ...;
|
Method method = ...;
|
||||||
|
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
context.setVariable("myFunction", method);
|
context.setVariable("myFunction", method);
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -1028,7 +1035,7 @@ The above method can then be registered and used as follows:
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
|
|
||||||
context.setVariable("reverseString",
|
context.setVariable("reverseString",
|
||||||
StringUtils.class.getDeclaredMethod("reverseString", String.class));
|
StringUtils.class.getDeclaredMethod("reverseString", String.class));
|
||||||
|
@ -1049,7 +1056,7 @@ lookup beans from an expression using the (@) symbol.
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
StandardEvaluationContext context = StandardEvaluationContext.create();
|
||||||
context.setBeanResolver(new MyBeanResolver());
|
context.setBeanResolver(new MyBeanResolver());
|
||||||
|
|
||||||
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
|
// 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"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
StandardEvaluationContext context = StandardEvaluationContext.create();
|
||||||
context.setBeanResolver(new MyBeanResolver());
|
context.setBeanResolver(new MyBeanResolver());
|
||||||
|
|
||||||
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
|
// 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();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
|
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);
|
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");
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
|
||||||
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
|
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
|
||||||
|
|
||||||
SimpleEvaluationContext context = new SimpleEvaluationContext();
|
SimpleEvaluationContext context = SimpleEvaluationContext.create();
|
||||||
|
|
||||||
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
|
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
|
||||||
System.out.println(city); // Smiljan
|
System.out.println(city); // Smiljan
|
||||||
|
|
Loading…
Reference in New Issue