Skip to content

Commit b7ff26a

Browse files
committed
Only cache resolved method when coming from ReflectiveMethodResolver
Issue: SPR-9495
1 parent 5e6044c commit b7ff26a

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.expression.spel.ExpressionState;
3333
import org.springframework.expression.spel.SpelEvaluationException;
3434
import org.springframework.expression.spel.SpelMessage;
35+
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
3536

3637
/**
3738
* Expression language AST node that represents a method reference.
@@ -88,7 +89,7 @@ private TypedValue getValueInternal(EvaluationContext evaluationContext,
8889
return TypedValue.NULL;
8990
}
9091

91-
MethodExecutor executorToUse = getCachedExecutor(value, targetType, argumentTypes);
92+
MethodExecutor executorToUse = getCachedExecutor(evaluationContext, value, targetType, argumentTypes);
9293
if (executorToUse != null) {
9394
try {
9495
return executorToUse.execute(evaluationContext, value, arguments);
@@ -104,8 +105,7 @@ private TypedValue getValueInternal(EvaluationContext evaluationContext,
104105

105106
// To determine the situation, the AccessException will contain a cause.
106107
// If the cause is an InvocationTargetException, a user exception was
107-
// thrown inside the method.
108-
// Otherwise the method could not be invoked.
108+
// thrown inside the method. Otherwise the method could not be invoked.
109109
throwSimpleExceptionIfPossible(value, ae);
110110

111111
// At this point we know it wasn't a user problem so worth a retry if a
@@ -161,7 +161,16 @@ private List<TypeDescriptor> getArgumentTypes(Object... arguments) {
161161
return Collections.unmodifiableList(descriptors);
162162
}
163163

164-
private MethodExecutor getCachedExecutor(Object value, TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
164+
private MethodExecutor getCachedExecutor(EvaluationContext evaluationContext, Object value,
165+
TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
166+
167+
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
168+
if (methodResolvers == null || methodResolvers.size() != 1 ||
169+
!(methodResolvers.get(0) instanceof ReflectiveMethodResolver)) {
170+
// Not a default ReflectiveMethodResolver - don't know whether caching is valid
171+
return null;
172+
}
173+
165174
CachedMethodExecutor executorToCheck = this.cachedExecutor;
166175
if (executorToCheck != null && executorToCheck.isSuitable(value, target, argumentTypes)) {
167176
return executorToCheck.get();

spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.junit.Test;
3333
import org.junit.rules.ExpectedException;
3434

35+
import org.springframework.core.MethodParameter;
3536
import org.springframework.core.convert.TypeDescriptor;
3637
import org.springframework.expression.AccessException;
3738
import org.springframework.expression.BeanResolver;
@@ -1777,6 +1778,47 @@ public void SPR_10452() throws Exception {
17771778
assertEquals(XYZ.Z, Array.get(result, 2));
17781779
}
17791780

1781+
@Test
1782+
public void SPR_9495() throws Exception {
1783+
SpelParserConfiguration configuration = new SpelParserConfiguration(false, false);
1784+
ExpressionParser parser = new SpelExpressionParser(configuration);
1785+
1786+
StandardEvaluationContext context = new StandardEvaluationContext();
1787+
Expression spel = parser.parseExpression("#enumType.values()");
1788+
1789+
context.setVariable("enumType", ABC.class);
1790+
Object result = spel.getValue(context);
1791+
assertNotNull(result);
1792+
assertTrue(result.getClass().isArray());
1793+
assertEquals(ABC.A, Array.get(result, 0));
1794+
assertEquals(ABC.B, Array.get(result, 1));
1795+
assertEquals(ABC.C, Array.get(result, 2));
1796+
1797+
context.addMethodResolver(new MethodResolver() {
1798+
@Override
1799+
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List<TypeDescriptor> argumentTypes) throws AccessException {
1800+
return new MethodExecutor() {
1801+
@Override
1802+
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
1803+
try {
1804+
Method method = XYZ.class.getMethod("values");
1805+
Object value = method.invoke(target, arguments);
1806+
return new TypedValue(value, new TypeDescriptor(new MethodParameter(method, -1)).narrow(value));
1807+
}
1808+
catch (Exception ex) {
1809+
throw new AccessException(ex.getMessage(), ex);
1810+
}
1811+
}
1812+
};
1813+
}
1814+
});
1815+
result = spel.getValue(context);
1816+
assertNotNull(result);
1817+
assertTrue(result.getClass().isArray());
1818+
assertEquals(XYZ.X, Array.get(result, 0));
1819+
assertEquals(XYZ.Y, Array.get(result, 1));
1820+
assertEquals(XYZ.Z, Array.get(result, 2));
1821+
}
17801822

17811823

17821824
private static enum ABC {A, B, C}

0 commit comments

Comments
 (0)