MethodBasedEvaluationContext reliably exposes varargs
Issue: SPR-14554
(cherry picked from commit fae503d
)
This commit is contained in:
parent
aade2d1ec9
commit
4543a28e51
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 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.
|
||||||
|
@ -17,8 +17,8 @@
|
||||||
package org.springframework.cache.interceptor;
|
package org.springframework.cache.interceptor;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
@ -38,25 +38,27 @@ import org.springframework.core.ParameterNameDiscoverer;
|
||||||
*
|
*
|
||||||
* @author Costin Leau
|
* @author Costin Leau
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
class CacheEvaluationContext extends MethodBasedEvaluationContext {
|
class CacheEvaluationContext extends MethodBasedEvaluationContext {
|
||||||
|
|
||||||
private final List<String> unavailableVariables;
|
private final Set<String> unavailableVariables = new HashSet<String>(1);
|
||||||
|
|
||||||
CacheEvaluationContext(Object rootObject, Method method, Object[] args,
|
|
||||||
ParameterNameDiscoverer paramDiscoverer) {
|
|
||||||
|
|
||||||
super(rootObject, method, args, paramDiscoverer);
|
CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
|
||||||
this.unavailableVariables = new ArrayList<String>();
|
ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||||
|
|
||||||
|
super(rootObject, method, arguments, parameterNameDiscoverer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the specified variable name as unavailable for that context. Any expression trying
|
* Add the specified variable name as unavailable for that context.
|
||||||
* to access this variable should lead to an exception.
|
* Any expression trying to access this variable should lead to an exception.
|
||||||
* <p>This permits the validation of expressions that could potentially a variable even
|
* <p>This permits the validation of expressions that could potentially a
|
||||||
* when such variable isn't available yet. Any expression trying to use that variable should
|
* variable even when such variable isn't available yet. Any expression
|
||||||
* therefore fail to evaluate.
|
* trying to use that variable should therefore fail to evaluate.
|
||||||
*/
|
*/
|
||||||
public void addUnavailableVariable(String name) {
|
public void addUnavailableVariable(String name) {
|
||||||
this.unavailableVariables.add(name);
|
this.unavailableVariables.add(name);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.context.expression;
|
package org.springframework.context.expression;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
@ -34,27 +35,27 @@ import org.springframework.util.ObjectUtils;
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Sergey Podgurskiy
|
* @author Juergen Hoeller
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
|
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
|
||||||
|
|
||||||
private final Method method;
|
private final Method method;
|
||||||
|
|
||||||
private final Object[] args;
|
private final Object[] arguments;
|
||||||
|
|
||||||
private final ParameterNameDiscoverer paramDiscoverer;
|
private final ParameterNameDiscoverer parameterNameDiscoverer;
|
||||||
|
|
||||||
private boolean paramLoaded = false;
|
private boolean argumentsLoaded = false;
|
||||||
|
|
||||||
|
|
||||||
public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] args,
|
public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
|
||||||
ParameterNameDiscoverer paramDiscoverer) {
|
ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||||
|
|
||||||
super(rootObject);
|
super(rootObject);
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.args = args;
|
this.arguments = arguments;
|
||||||
this.paramDiscoverer = paramDiscoverer;
|
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,9 +65,9 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
|
||||||
if (variable != null) {
|
if (variable != null) {
|
||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
if (!this.paramLoaded) {
|
if (!this.argumentsLoaded) {
|
||||||
lazyLoadArguments();
|
lazyLoadArguments();
|
||||||
this.paramLoaded = true;
|
this.argumentsLoaded = true;
|
||||||
variable = super.lookupVariable(name);
|
variable = super.lookupVariable(name);
|
||||||
}
|
}
|
||||||
return variable;
|
return variable;
|
||||||
|
@ -76,22 +77,30 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
|
||||||
* Load the param information only when needed.
|
* Load the param information only when needed.
|
||||||
*/
|
*/
|
||||||
protected void lazyLoadArguments() {
|
protected void lazyLoadArguments() {
|
||||||
// shortcut if no args need to be loaded
|
// Shortcut if no args need to be loaded
|
||||||
if (ObjectUtils.isEmpty(this.args)) {
|
if (ObjectUtils.isEmpty(this.arguments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save arguments as indexed variables
|
// Expose indexed variables as well as parameter names (if discoverable)
|
||||||
for (int i = 0; i < this.args.length; i++) {
|
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
|
||||||
setVariable("a" + i, this.args[i]);
|
int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterTypes().length);
|
||||||
setVariable("p" + i, this.args[i]);
|
int argsCount = this.arguments.length;
|
||||||
}
|
|
||||||
|
|
||||||
String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method);
|
for (int i = 0; i < paramCount; i++) {
|
||||||
// save parameter names (if discovered)
|
Object value = null;
|
||||||
if (parameterNames != null) {
|
if (argsCount > paramCount && i == paramCount - 1) {
|
||||||
for (int i = 0; i < this.args.length; i++) {
|
// Expose remaining arguments as vararg array for last parameter
|
||||||
setVariable(parameterNames[i], this.args[i]);
|
value = Arrays.copyOfRange(this.arguments, i, argsCount);
|
||||||
|
}
|
||||||
|
else if (argsCount > i) {
|
||||||
|
// Actual argument found - otherwise left as null
|
||||||
|
value = this.arguments[i];
|
||||||
|
}
|
||||||
|
setVariable("a" + i, value);
|
||||||
|
setVariable("p" + i, value);
|
||||||
|
if (paramNames != null) {
|
||||||
|
setVariable(paramNames[i], value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,17 +30,18 @@ import static org.junit.Assert.*;
|
||||||
* Unit tests for {@link MethodBasedEvaluationContext}.
|
* Unit tests for {@link MethodBasedEvaluationContext}.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @author Sergey Podgurskiy
|
* @author Sergey Podgurskiy
|
||||||
*/
|
*/
|
||||||
public class MethodBasedEvaluationContextTests {
|
public class MethodBasedEvaluationContextTests {
|
||||||
|
|
||||||
private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
|
private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleArguments() {
|
public void simpleArguments() {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
|
||||||
String.class, Boolean.class);
|
MethodBasedEvaluationContext context = createEvaluationContext(method, "test", true);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {"test", true});
|
|
||||||
|
|
||||||
assertEquals("test", context.lookupVariable("a0"));
|
assertEquals("test", context.lookupVariable("a0"));
|
||||||
assertEquals("test", context.lookupVariable("p0"));
|
assertEquals("test", context.lookupVariable("p0"));
|
||||||
|
@ -51,16 +52,21 @@ public class MethodBasedEvaluationContextTests {
|
||||||
assertEquals(true, context.lookupVariable("flag"));
|
assertEquals(true, context.lookupVariable("flag"));
|
||||||
|
|
||||||
assertNull(context.lookupVariable("a2"));
|
assertNull(context.lookupVariable("a2"));
|
||||||
|
assertNull(context.lookupVariable("p2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nullArgument() {
|
public void nullArgument() {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
|
||||||
String.class, Boolean.class);
|
MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
|
|
||||||
|
|
||||||
assertNull(context.lookupVariable("a0"));
|
assertNull(context.lookupVariable("a0"));
|
||||||
assertNull(context.lookupVariable("p0"));
|
assertNull(context.lookupVariable("p0"));
|
||||||
|
assertNull(context.lookupVariable("foo"));
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a1"));
|
||||||
|
assertNull(context.lookupVariable("p1"));
|
||||||
|
assertNull(context.lookupVariable("flag"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -68,39 +74,58 @@ public class MethodBasedEvaluationContextTests {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
|
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a0"));
|
||||||
assertNull(context.lookupVariable("p0"));
|
assertNull(context.lookupVariable("p0"));
|
||||||
|
assertNull(context.lookupVariable("flag"));
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a1"));
|
||||||
assertNull(context.lookupVariable("p1"));
|
assertNull(context.lookupVariable("p1"));
|
||||||
|
assertNull(context.lookupVariable("vararg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void varArgNull() {
|
public void varArgNull() {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
|
MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a0"));
|
||||||
assertNull(context.lookupVariable("p0"));
|
assertNull(context.lookupVariable("p0"));
|
||||||
|
assertNull(context.lookupVariable("flag"));
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a1"));
|
||||||
assertNull(context.lookupVariable("p1"));
|
assertNull(context.lookupVariable("p1"));
|
||||||
|
assertNull(context.lookupVariable("vararg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void varArgSingle() {
|
public void varArgSingle() {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, "hello"});
|
MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello");
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a0"));
|
||||||
assertNull(context.lookupVariable("p0"));
|
assertNull(context.lookupVariable("p0"));
|
||||||
|
assertNull(context.lookupVariable("flag"));
|
||||||
|
|
||||||
|
assertEquals("hello", context.lookupVariable("a1"));
|
||||||
assertEquals("hello", context.lookupVariable("p1"));
|
assertEquals("hello", context.lookupVariable("p1"));
|
||||||
|
assertEquals("hello", context.lookupVariable("vararg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void varArgMultiple() {
|
public void varArgMultiple() {
|
||||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
|
||||||
MethodBasedEvaluationContext context = createEvaluationContext(method,
|
MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello", "hi");
|
||||||
new Object[] {null, new String[]{"hello", "hi"}});
|
|
||||||
|
|
||||||
|
assertNull(context.lookupVariable("a0"));
|
||||||
assertNull(context.lookupVariable("p0"));
|
assertNull(context.lookupVariable("p0"));
|
||||||
assertArrayEquals(new String[]{"hello", "hi"}, (String[]) context.lookupVariable("p1"));
|
assertNull(context.lookupVariable("flag"));
|
||||||
|
|
||||||
|
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("a1"));
|
||||||
|
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("p1"));
|
||||||
|
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("vararg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) {
|
private MethodBasedEvaluationContext createEvaluationContext(Method method, Object... args) {
|
||||||
return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
|
return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,9 +137,7 @@ public class MethodBasedEvaluationContextTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hello(Boolean flag, String... vararg){
|
private void hello(Boolean flag, String... vararg){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue