SimpleEvaluationContext.Builder withMethodResolvers/withInstanceMethods
Includes DataBindingMethodResolver as ReflectiveMethodResolver subclass. Issue: SPR-16588
This commit is contained in:
parent
a989ea0867
commit
9128226da4
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -41,6 +41,11 @@ public interface EvaluationContext {
|
||||||
*/
|
*/
|
||||||
TypedValue getRootObject();
|
TypedValue getRootObject();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of accessors that will be asked in turn to read/write a property.
|
||||||
|
*/
|
||||||
|
List<PropertyAccessor> getPropertyAccessors();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of resolvers that will be asked in turn to locate a constructor.
|
* Return a list of resolvers that will be asked in turn to locate a constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -52,9 +57,10 @@ public interface EvaluationContext {
|
||||||
List<MethodResolver> getMethodResolvers();
|
List<MethodResolver> getMethodResolvers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of accessors that will be asked in turn to read/write a property.
|
* Return a bean resolver that can look up beans by name.
|
||||||
*/
|
*/
|
||||||
List<PropertyAccessor> getPropertyAccessors();
|
@Nullable
|
||||||
|
BeanResolver getBeanResolver();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a type locator that can be used to find types, either by short or
|
* Return a type locator that can be used to find types, either by short or
|
||||||
|
@ -78,12 +84,6 @@ public interface EvaluationContext {
|
||||||
*/
|
*/
|
||||||
OperatorOverloader getOperatorOverloader();
|
OperatorOverloader getOperatorOverloader();
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a bean resolver that can look up beans by name.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
BeanResolver getBeanResolver();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a named variable within this evaluation context to a specified value.
|
* Set a named variable within this evaluation context to a specified value.
|
||||||
* @param name variable to set
|
* @param name variable to set
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.expression.spel.support;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.expression.AccessException;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.MethodExecutor;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link org.springframework.expression.MethodResolver} variant for data binding
|
||||||
|
* purposes, using reflection to access instance methods on a given target object.
|
||||||
|
*
|
||||||
|
* <p>This accessor does not resolve static methods and also no technical methods
|
||||||
|
* on {@code java.lang.Object} or {@code java.lang.Class}.
|
||||||
|
* For unrestricted resolution, choose {@link ReflectiveMethodResolver} instead.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.3.15
|
||||||
|
* @see #forInstanceMethodInvocation()
|
||||||
|
* @see DataBindingPropertyAccessor
|
||||||
|
*/
|
||||||
|
public class DataBindingMethodResolver extends ReflectiveMethodResolver {
|
||||||
|
|
||||||
|
private DataBindingMethodResolver() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
|
||||||
|
List<TypeDescriptor> argumentTypes) throws AccessException {
|
||||||
|
|
||||||
|
if (targetObject instanceof Class) {
|
||||||
|
throw new IllegalArgumentException("DataBindingMethodResolver does not support Class targets");
|
||||||
|
}
|
||||||
|
return super.resolve(context, targetObject, name, argumentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCandidateForInvocation(Method method, Class<?> targetClass) {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Class<?> clazz = method.getDeclaringClass();
|
||||||
|
return (clazz != Object.class && clazz != Class.class && !ClassLoader.class.isAssignableFrom(targetClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new data-binding method resolver for instance method resolution.
|
||||||
|
*/
|
||||||
|
public static DataBindingMethodResolver forInstanceMethodInvocation() {
|
||||||
|
return new DataBindingMethodResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,8 +20,6 @@ import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
@ -82,6 +80,12 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a filter for methods on the given type.
|
||||||
|
* @param type the type to filter on
|
||||||
|
* @param filter the corresponding method filter,
|
||||||
|
* or {@code null} to clear any filter for the given type
|
||||||
|
*/
|
||||||
public void registerMethodFilter(Class<?> type, @Nullable MethodFilter filter) {
|
public void registerMethodFilter(Class<?> type, @Nullable MethodFilter filter) {
|
||||||
if (this.filters == null) {
|
if (this.filters == null) {
|
||||||
this.filters = new HashMap<>();
|
this.filters = new HashMap<>();
|
||||||
|
@ -94,7 +98,6 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locate a method on a type. There are three kinds of match that might occur:
|
* Locate a method on a type. There are three kinds of match that might occur:
|
||||||
* <ol>
|
* <ol>
|
||||||
|
@ -112,13 +115,13 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
try {
|
try {
|
||||||
TypeConverter typeConverter = context.getTypeConverter();
|
TypeConverter typeConverter = context.getTypeConverter();
|
||||||
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
||||||
List<Method> methods = new ArrayList<>(getMethods(type, targetObject));
|
ArrayList<Method> methods = new ArrayList<>(getMethods(type, targetObject));
|
||||||
|
|
||||||
// If a filter is registered for this type, call it
|
// If a filter is registered for this type, call it
|
||||||
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
List<Method> filtered = filter.filter(methods);
|
List<Method> filtered = filter.filter(methods);
|
||||||
methods = (filtered instanceof ArrayList ? filtered : new ArrayList<>(filtered));
|
methods = (filtered instanceof ArrayList ? (ArrayList<Method>) filtered : new ArrayList<>(filtered));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort methods into a sensible order
|
// Sort methods into a sensible order
|
||||||
|
@ -126,7 +129,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
methods.sort((m1, m2) -> {
|
methods.sort((m1, m2) -> {
|
||||||
int m1pl = m1.getParameterCount();
|
int m1pl = m1.getParameterCount();
|
||||||
int m2pl = m2.getParameterCount();
|
int m2pl = m2.getParameterCount();
|
||||||
// varargs methods go last
|
// vararg methods go last
|
||||||
if (m1pl == m2pl) {
|
if (m1pl == m2pl) {
|
||||||
if (!m1.isVarArgs() && m2.isVarArgs()) {
|
if (!m1.isVarArgs() && m2.isVarArgs()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -218,7 +221,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Method> getMethods(Class<?> type, Object targetObject) {
|
private Set<Method> getMethods(Class<?> type, Object targetObject) {
|
||||||
if (targetObject instanceof Class) {
|
if (targetObject instanceof Class) {
|
||||||
Set<Method> result = new LinkedHashSet<>();
|
Set<Method> result = new LinkedHashSet<>();
|
||||||
// Add these so that static methods are invocable on the type: e.g. Float.valueOf(..)
|
// Add these so that static methods are invocable on the type: e.g. Float.valueOf(..)
|
||||||
|
@ -236,12 +239,24 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
Set<Method> result = new LinkedHashSet<>();
|
Set<Method> result = new LinkedHashSet<>();
|
||||||
// Expose interface methods (not proxy-declared overrides) for proper vararg introspection
|
// Expose interface methods (not proxy-declared overrides) for proper vararg introspection
|
||||||
for (Class<?> ifc : type.getInterfaces()) {
|
for (Class<?> ifc : type.getInterfaces()) {
|
||||||
Collections.addAll(result, getMethods(ifc));
|
Method[] methods = getMethods(ifc);
|
||||||
|
for (Method method : methods) {
|
||||||
|
if (isCandidateForInvocation(method, type)) {
|
||||||
|
result.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Arrays.asList(getMethods(type));
|
Set<Method> result = new LinkedHashSet<>();
|
||||||
|
Method[] methods = getMethods(type);
|
||||||
|
for (Method method : methods) {
|
||||||
|
if (isCandidateForInvocation(method, type)) {
|
||||||
|
result.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,4 +272,17 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
return type.getMethods();
|
return type.getMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given {@code Method} is a candidate for method resolution
|
||||||
|
* on an instance of the given target class.
|
||||||
|
* <p>The default implementation considers any method as a candidate, even for
|
||||||
|
* static methods sand non-user-declared methods on the {@link Object} base class.
|
||||||
|
* @param method the Method to evaluate
|
||||||
|
* @param targetClass the concrete target class that is being introspected
|
||||||
|
* @since 4.3.15
|
||||||
|
*/
|
||||||
|
protected boolean isCandidateForInvocation(Method method, Class<?> targetClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,7 +421,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the given {@code Method} is a candidate for property access.
|
* Determine whether the given {@code Method} is a candidate for property access
|
||||||
|
* on an instance of the given target class.
|
||||||
* <p>The default implementation considers any method as a candidate, even for
|
* <p>The default implementation considers any method as a candidate, even for
|
||||||
* non-user-declared properties on the {@link Object} base class.
|
* non-user-declared properties on the {@link Object} base class.
|
||||||
* @param method the Method to evaluate
|
* @param method the Method to evaluate
|
||||||
|
|
|
@ -92,6 +92,8 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
private final List<PropertyAccessor> propertyAccessors;
|
private final List<PropertyAccessor> propertyAccessors;
|
||||||
|
|
||||||
|
private final List<MethodResolver> methodResolvers;
|
||||||
|
|
||||||
private final TypeConverter typeConverter;
|
private final TypeConverter typeConverter;
|
||||||
|
|
||||||
private final TypeComparator typeComparator = new StandardTypeComparator();
|
private final TypeComparator typeComparator = new StandardTypeComparator();
|
||||||
|
@ -101,8 +103,11 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
private final Map<String, Object> variables = new HashMap<>();
|
private final Map<String, Object> variables = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
private SimpleEvaluationContext(List<PropertyAccessor> accessors, @Nullable TypeConverter converter) {
|
private SimpleEvaluationContext(List<PropertyAccessor> accessors, List<MethodResolver> resolvers,
|
||||||
|
@Nullable TypeConverter converter) {
|
||||||
|
|
||||||
this.propertyAccessors = accessors;
|
this.propertyAccessors = accessors;
|
||||||
|
this.methodResolvers = resolvers;
|
||||||
this.typeConverter = (converter != null ? converter : new StandardTypeConverter());
|
this.typeConverter = (converter != null ? converter : new StandardTypeConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +124,10 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
return TypedValue.NULL;
|
return TypedValue.NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the specified {@link PropertyAccessor} delegates, if any.
|
||||||
|
* @see #forPropertyAccessors
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<PropertyAccessor> getPropertyAccessors() {
|
public List<PropertyAccessor> getPropertyAccessors() {
|
||||||
return this.propertyAccessors;
|
return this.propertyAccessors;
|
||||||
|
@ -134,16 +143,17 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a single {@link ReflectiveMethodResolver}.
|
* Return the specified {@link MethodResolver} delegates, if any.
|
||||||
|
* @see Builder#withMethodResolvers
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<MethodResolver> getMethodResolvers() {
|
public List<MethodResolver> getMethodResolvers() {
|
||||||
return Collections.emptyList();
|
return this.methodResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code SimpleEvaluationContext} does not support use of bean references.
|
* {@code SimpleEvaluationContext} does not support the use of bean references.
|
||||||
* @return Always returns {@code null}
|
* @return always {@code null}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -164,6 +174,8 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
/**
|
/**
|
||||||
* The configured {@link TypeConverter}.
|
* The configured {@link TypeConverter}.
|
||||||
* <p>By default this is {@link StandardTypeConverter}.
|
* <p>By default this is {@link StandardTypeConverter}.
|
||||||
|
* @see Builder#withTypeConverter
|
||||||
|
* @see Builder#withConversionService
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TypeConverter getTypeConverter() {
|
public TypeConverter getTypeConverter() {
|
||||||
|
@ -203,6 +215,7 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
|
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
|
||||||
* (e.g. attribute resolution in a custom data structure), potentially combined with
|
* (e.g. attribute resolution in a custom data structure), potentially combined with
|
||||||
* a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
|
* a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
|
||||||
|
* @param accessors the accessor delegates to use
|
||||||
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
|
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
|
||||||
* @see DataBindingPropertyAccessor#forReadWriteAccess()
|
* @see DataBindingPropertyAccessor#forReadWriteAccess()
|
||||||
*/
|
*/
|
||||||
|
@ -242,13 +255,46 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private final List<PropertyAccessor> propertyAccessors;
|
private final List<PropertyAccessor> accessors;
|
||||||
|
|
||||||
|
private List<MethodResolver> resolvers = Collections.emptyList();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private TypeConverter typeConverter;
|
private TypeConverter typeConverter;
|
||||||
|
|
||||||
public Builder(PropertyAccessor... accessors) {
|
public Builder(PropertyAccessor... accessors) {
|
||||||
this.propertyAccessors = Arrays.asList(accessors);
|
this.accessors = Arrays.asList(accessors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the specified {@link MethodResolver} delegates for
|
||||||
|
* a combination of property access and method resolution.
|
||||||
|
* @param resolvers the resolver delegates to use
|
||||||
|
* @see #withInstanceMethods()
|
||||||
|
* @see SimpleEvaluationContext#forPropertyAccessors
|
||||||
|
*/
|
||||||
|
public Builder withMethodResolvers(MethodResolver... resolvers) {
|
||||||
|
for (MethodResolver resolver : resolvers) {
|
||||||
|
if (resolver.getClass() == ReflectiveMethodResolver.class) {
|
||||||
|
throw new IllegalArgumentException("SimpleEvaluationContext is not designed for use with a plain " +
|
||||||
|
"ReflectiveMethodResolver. Consider using DataBindingMethodResolver or a custom subclass.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.resolvers = Arrays.asList(resolvers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a {@link DataBindingMethodResolver} for instance method invocation purposes
|
||||||
|
* (i.e. not supporting static methods) in addition to the specified property accessors,
|
||||||
|
* typically in combination with a {@link DataBindingPropertyAccessor}.
|
||||||
|
* @see #withMethodResolvers
|
||||||
|
* @see SimpleEvaluationContext#forReadOnlyDataBinding()
|
||||||
|
* @see SimpleEvaluationContext#forReadWriteDataBinding()
|
||||||
|
*/
|
||||||
|
public Builder withInstanceMethods() {
|
||||||
|
this.resolvers = Collections.singletonList(DataBindingMethodResolver.forInstanceMethodInvocation());
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -276,7 +322,7 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleEvaluationContext build() {
|
public SimpleEvaluationContext build() {
|
||||||
return new SimpleEvaluationContext(this.propertyAccessors, this.typeConverter);
|
return new SimpleEvaluationContext(this.accessors, this.resolvers, this.typeConverter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import org.springframework.expression.PropertyAccessor;
|
||||||
import org.springframework.expression.TypedValue;
|
import org.springframework.expression.TypedValue;
|
||||||
import org.springframework.expression.spel.standard.SpelExpression;
|
import org.springframework.expression.spel.standard.SpelExpression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
|
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.expression.spel.testresources.Person;
|
import org.springframework.expression.spel.testresources.Person;
|
||||||
|
|
||||||
|
@ -186,22 +186,38 @@ public class PropertyAccessTests extends AbstractExpressionTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void standardGetClassAccess() {
|
public void standardGetClassAccess() {
|
||||||
Expression expr = parser.parseExpression("'a'.class.getName()");
|
assertEquals(String.class.getName(), parser.parseExpression("'a'.class.name").getValue());
|
||||||
assertEquals(String.class.getName(), expr.getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = SpelEvaluationException.class)
|
@Test(expected = SpelEvaluationException.class)
|
||||||
public void noGetClassAccess() {
|
public void noGetClassAccess() {
|
||||||
Expression expr = parser.parseExpression("'a'.class.getName()");
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
|
||||||
context.setPropertyAccessors(Collections.singletonList(DataBindingPropertyAccessor.forReadWriteAccess()));
|
parser.parseExpression("'a'.class.name").getValue(context);
|
||||||
expr.getValue(context);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void propertyReadOnly() {
|
||||||
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||||
|
|
||||||
|
Expression expr = parser.parseExpression("name");
|
||||||
|
Person target = new Person("p1");
|
||||||
|
assertEquals("p1", expr.getValue(context, target));
|
||||||
|
target.setName("p2");
|
||||||
|
assertEquals("p2", expr.getValue(context, target));
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser.parseExpression("name='p3'").getValue(context, target);
|
||||||
|
fail("Should have thrown SpelEvaluationException");
|
||||||
|
}
|
||||||
|
catch (SpelEvaluationException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void propertyReadWrite() {
|
public void propertyReadWrite() {
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
|
||||||
context.setPropertyAccessors(Collections.singletonList(DataBindingPropertyAccessor.forReadWriteAccess()));
|
|
||||||
|
|
||||||
Expression expr = parser.parseExpression("name");
|
Expression expr = parser.parseExpression("name");
|
||||||
Person target = new Person("p1");
|
Person target = new Person("p1");
|
||||||
|
@ -218,18 +234,26 @@ public class PropertyAccessTests extends AbstractExpressionTests {
|
||||||
assertEquals("p4", expr.getValue(context, target));
|
assertEquals("p4", expr.getValue(context, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = SpelEvaluationException.class)
|
@Test
|
||||||
public void propertyReadOnly() {
|
public void propertyAccessWithoutMethodResolver() {
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||||
context.setPropertyAccessors(Collections.singletonList(DataBindingPropertyAccessor.forReadOnlyAccess()));
|
|
||||||
|
|
||||||
Expression expr = parser.parseExpression("name");
|
|
||||||
Person target = new Person("p1");
|
Person target = new Person("p1");
|
||||||
assertEquals("p1", expr.getValue(context, target));
|
try {
|
||||||
target.setName("p2");
|
parser.parseExpression("name.substring(1)").getValue(context, target);
|
||||||
assertEquals("p2", expr.getValue(context, target));
|
fail("Should have thrown SpelEvaluationException");
|
||||||
|
}
|
||||||
|
catch (SpelEvaluationException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parser.parseExpression("name='p3'").getValue(context, target);
|
@Test
|
||||||
|
public void propertyAccessWithInstanceMethodResolver() {
|
||||||
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
|
||||||
|
|
||||||
|
Person target = new Person("p1");
|
||||||
|
assertEquals("1", parser.parseExpression("name.substring(1)").getValue(context, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue