Skip to content

Commit 6de67cc

Browse files
committed
Only cache resolved method when coming from ReflectiveMethodResolver
Issue: SPR-9495 (cherry picked from commit b7ff26a)
1 parent b9d726f commit 6de67cc

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;
@@ -1775,6 +1776,47 @@ public void SPR_10452() throws Exception {
17751776
assertEquals(XYZ.Z, Array.get(result, 2));
17761777
}
17771778

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

17791821

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

0 commit comments

Comments
 (0)