Skip to content

Commit 48980a2

Browse files
committed
MethodBasedEvaluationContext reliably exposes varargs
Issue: SPR-14554 (cherry picked from commit 4543a28)
1 parent 9475c06 commit 48980a2

File tree

3 files changed

+120
-43
lines changed

3 files changed

+120
-43
lines changed

spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvaluationContext.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
1717
package org.springframework.cache.interceptor;
1818

1919
import java.lang.reflect.Method;
20-
import java.util.ArrayList;
21-
import java.util.List;
20+
import java.util.HashSet;
21+
import java.util.Set;
2222

2323
import org.springframework.context.expression.MethodBasedEvaluationContext;
2424
import org.springframework.core.ParameterNameDiscoverer;
@@ -38,25 +38,27 @@
3838
*
3939
* @author Costin Leau
4040
* @author Stephane Nicoll
41+
* @author Juergen Hoeller
4142
* @since 3.1
4243
*/
4344
class CacheEvaluationContext extends MethodBasedEvaluationContext {
4445

45-
private final List<String> unavailableVariables;
46+
private final Set<String> unavailableVariables = new HashSet<String>(1);
4647

47-
CacheEvaluationContext(Object rootObject, Method method, Object[] args,
48-
ParameterNameDiscoverer paramDiscoverer) {
4948

50-
super(rootObject, method, args, paramDiscoverer);
51-
this.unavailableVariables = new ArrayList<String>();
49+
CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
50+
ParameterNameDiscoverer parameterNameDiscoverer) {
51+
52+
super(rootObject, method, arguments, parameterNameDiscoverer);
5253
}
5354

55+
5456
/**
55-
* Add the specified variable name as unavailable for that context. Any expression trying
56-
* to access this variable should lead to an exception.
57-
* <p>This permits the validation of expressions that could potentially a variable even
58-
* when such variable isn't available yet. Any expression trying to use that variable should
59-
* therefore fail to evaluate.
57+
* Add the specified variable name as unavailable for that context.
58+
* Any expression trying to access this variable should lead to an exception.
59+
* <p>This permits the validation of expressions that could potentially a
60+
* variable even when such variable isn't available yet. Any expression
61+
* trying to use that variable should therefore fail to evaluate.
6062
*/
6163
public void addUnavailableVariable(String name) {
6264
this.unavailableVariables.add(name);

spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java

+31-21
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.context.expression;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.Arrays;
2021

2122
import org.springframework.core.ParameterNameDiscoverer;
2223
import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -34,26 +35,27 @@
3435
* </ol>
3536
*
3637
* @author Stephane Nicoll
38+
* @author Juergen Hoeller
3739
* @since 4.2
3840
*/
3941
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
4042

4143
private final Method method;
4244

43-
private final Object[] args;
45+
private final Object[] arguments;
4446

45-
private final ParameterNameDiscoverer paramDiscoverer;
47+
private final ParameterNameDiscoverer parameterNameDiscoverer;
4648

47-
private boolean paramLoaded = false;
49+
private boolean argumentsLoaded = false;
4850

4951

50-
public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] args,
51-
ParameterNameDiscoverer paramDiscoverer) {
52+
public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
53+
ParameterNameDiscoverer parameterNameDiscoverer) {
5254

5355
super(rootObject);
5456
this.method = method;
55-
this.args = args;
56-
this.paramDiscoverer = paramDiscoverer;
57+
this.arguments = arguments;
58+
this.parameterNameDiscoverer = parameterNameDiscoverer;
5759
}
5860

5961

@@ -63,9 +65,9 @@ public Object lookupVariable(String name) {
6365
if (variable != null) {
6466
return variable;
6567
}
66-
if (!this.paramLoaded) {
68+
if (!this.argumentsLoaded) {
6769
lazyLoadArguments();
68-
this.paramLoaded = true;
70+
this.argumentsLoaded = true;
6971
variable = super.lookupVariable(name);
7072
}
7173
return variable;
@@ -75,22 +77,30 @@ public Object lookupVariable(String name) {
7577
* Load the param information only when needed.
7678
*/
7779
protected void lazyLoadArguments() {
78-
// shortcut if no args need to be loaded
79-
if (ObjectUtils.isEmpty(this.args)) {
80+
// Shortcut if no args need to be loaded
81+
if (ObjectUtils.isEmpty(this.arguments)) {
8082
return;
8183
}
8284

83-
// save arguments as indexed variables
84-
for (int i = 0; i < this.args.length; i++) {
85-
setVariable("a" + i, this.args[i]);
86-
setVariable("p" + i, this.args[i]);
87-
}
85+
// Expose indexed variables as well as parameter names (if discoverable)
86+
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
87+
int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterTypes().length);
88+
int argsCount = this.arguments.length;
8889

89-
String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method);
90-
// save parameter names (if discovered)
91-
if (parameterNames != null) {
92-
for (int i = 0; i < parameterNames.length; i++) {
93-
setVariable(parameterNames[i], this.args[i]);
90+
for (int i = 0; i < paramCount; i++) {
91+
Object value = null;
92+
if (argsCount > paramCount && i == paramCount - 1) {
93+
// Expose remaining arguments as vararg array for last parameter
94+
value = Arrays.copyOfRange(this.arguments, i, argsCount);
95+
}
96+
else if (argsCount > i) {
97+
// Actual argument found - otherwise left as null
98+
value = this.arguments[i];
99+
}
100+
setVariable("a" + i, value);
101+
setVariable("p" + i, value);
102+
if (paramNames != null) {
103+
setVariable(paramNames[i], value);
94104
}
95105
}
96106
}

spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java

+74-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,16 +30,18 @@
3030
* Unit tests for {@link MethodBasedEvaluationContext}.
3131
*
3232
* @author Stephane Nicoll
33+
* @author Juergen Hoeller
34+
* @author Sergey Podgurskiy
3335
*/
3436
public class MethodBasedEvaluationContextTests {
3537

3638
private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
3739

40+
3841
@Test
3942
public void simpleArguments() {
40-
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
41-
String.class, Boolean.class);
42-
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {"test", true});
43+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
44+
MethodBasedEvaluationContext context = createEvaluationContext(method, "test", true);
4345

4446
assertEquals("test", context.lookupVariable("a0"));
4547
assertEquals("test", context.lookupVariable("p0"));
@@ -50,19 +52,80 @@ public void simpleArguments() {
5052
assertEquals(true, context.lookupVariable("flag"));
5153

5254
assertNull(context.lookupVariable("a2"));
55+
assertNull(context.lookupVariable("p2"));
5356
}
5457

5558
@Test
5659
public void nullArgument() {
57-
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
58-
String.class, Boolean.class);
59-
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
60+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
61+
MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
62+
63+
assertNull(context.lookupVariable("a0"));
64+
assertNull(context.lookupVariable("p0"));
65+
assertNull(context.lookupVariable("foo"));
66+
67+
assertNull(context.lookupVariable("a1"));
68+
assertNull(context.lookupVariable("p1"));
69+
assertNull(context.lookupVariable("flag"));
70+
}
71+
72+
@Test
73+
public void varArgEmpty() {
74+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
75+
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
6076

6177
assertNull(context.lookupVariable("a0"));
6278
assertNull(context.lookupVariable("p0"));
79+
assertNull(context.lookupVariable("flag"));
80+
81+
assertNull(context.lookupVariable("a1"));
82+
assertNull(context.lookupVariable("p1"));
83+
assertNull(context.lookupVariable("vararg"));
84+
}
85+
86+
@Test
87+
public void varArgNull() {
88+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
89+
MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
90+
91+
assertNull(context.lookupVariable("a0"));
92+
assertNull(context.lookupVariable("p0"));
93+
assertNull(context.lookupVariable("flag"));
94+
95+
assertNull(context.lookupVariable("a1"));
96+
assertNull(context.lookupVariable("p1"));
97+
assertNull(context.lookupVariable("vararg"));
6398
}
6499

65-
private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) {
100+
@Test
101+
public void varArgSingle() {
102+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
103+
MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello");
104+
105+
assertNull(context.lookupVariable("a0"));
106+
assertNull(context.lookupVariable("p0"));
107+
assertNull(context.lookupVariable("flag"));
108+
109+
assertEquals("hello", context.lookupVariable("a1"));
110+
assertEquals("hello", context.lookupVariable("p1"));
111+
assertEquals("hello", context.lookupVariable("vararg"));
112+
}
113+
114+
@Test
115+
public void varArgMultiple() {
116+
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
117+
MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello", "hi");
118+
119+
assertNull(context.lookupVariable("a0"));
120+
assertNull(context.lookupVariable("p0"));
121+
assertNull(context.lookupVariable("flag"));
122+
123+
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("a1"));
124+
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("p1"));
125+
assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("vararg"));
126+
}
127+
128+
private MethodBasedEvaluationContext createEvaluationContext(Method method, Object... args) {
66129
return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
67130
}
68131

@@ -73,6 +136,8 @@ private static class SampleMethods {
73136
private void hello(String foo, Boolean flag) {
74137
}
75138

139+
private void hello(Boolean flag, String... vararg){
140+
}
76141
}
77142

78-
}
143+
}

0 commit comments

Comments
 (0)