From 6de67cc2df9fc888ba00980b5f80b0019e38764c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 4 Sep 2013 16:43:51 +0200 Subject: [PATCH] Only cache resolved method when coming from ReflectiveMethodResolver Issue: SPR-9495 (cherry picked from commit b7ff26a) --- .../expression/spel/ast/MethodReference.java | 17 ++++++-- .../expression/spel/SpelReproTests.java | 42 +++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index f3f4502846..dc40f77c3d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -32,6 +32,7 @@ import org.springframework.expression.TypedValue; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelMessage; +import org.springframework.expression.spel.support.ReflectiveMethodResolver; /** * Expression language AST node that represents a method reference. @@ -88,7 +89,7 @@ public class MethodReference extends SpelNodeImpl { return TypedValue.NULL; } - MethodExecutor executorToUse = getCachedExecutor(value, targetType, argumentTypes); + MethodExecutor executorToUse = getCachedExecutor(evaluationContext, value, targetType, argumentTypes); if (executorToUse != null) { try { return executorToUse.execute(evaluationContext, value, arguments); @@ -104,8 +105,7 @@ public class MethodReference extends SpelNodeImpl { // To determine the situation, the AccessException will contain a cause. // If the cause is an InvocationTargetException, a user exception was - // thrown inside the method. - // Otherwise the method could not be invoked. + // thrown inside the method. Otherwise the method could not be invoked. throwSimpleExceptionIfPossible(value, ae); // At this point we know it wasn't a user problem so worth a retry if a @@ -161,7 +161,16 @@ public class MethodReference extends SpelNodeImpl { return Collections.unmodifiableList(descriptors); } - private MethodExecutor getCachedExecutor(Object value, TypeDescriptor target, List argumentTypes) { + private MethodExecutor getCachedExecutor(EvaluationContext evaluationContext, Object value, + TypeDescriptor target, List argumentTypes) { + + List methodResolvers = evaluationContext.getMethodResolvers(); + if (methodResolvers == null || methodResolvers.size() != 1 || + !(methodResolvers.get(0) instanceof ReflectiveMethodResolver)) { + // Not a default ReflectiveMethodResolver - don't know whether caching is valid + return null; + } + CachedMethodExecutor executorToCheck = this.cachedExecutor; if (executorToCheck != null && executorToCheck.isSuitable(value, target, argumentTypes)) { return executorToCheck.get(); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index 060912f90b..b63a78ffd1 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -32,6 +32,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; import org.springframework.expression.BeanResolver; @@ -1775,6 +1776,47 @@ public class SpelReproTests extends ExpressionTestCase { assertEquals(XYZ.Z, Array.get(result, 2)); } + @Test + public void SPR_9495() throws Exception { + SpelParserConfiguration configuration = new SpelParserConfiguration(false, false); + ExpressionParser parser = new SpelExpressionParser(configuration); + + StandardEvaluationContext context = new StandardEvaluationContext(); + Expression spel = parser.parseExpression("#enumType.values()"); + + context.setVariable("enumType", ABC.class); + Object result = spel.getValue(context); + assertNotNull(result); + assertTrue(result.getClass().isArray()); + assertEquals(ABC.A, Array.get(result, 0)); + assertEquals(ABC.B, Array.get(result, 1)); + assertEquals(ABC.C, Array.get(result, 2)); + + context.addMethodResolver(new MethodResolver() { + @Override + public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List argumentTypes) throws AccessException { + return new MethodExecutor() { + @Override + public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { + try { + Method method = XYZ.class.getMethod("values"); + Object value = method.invoke(target, arguments); + return new TypedValue(value, new TypeDescriptor(new MethodParameter(method, -1)).narrow(value)); + } + catch (Exception ex) { + throw new AccessException(ex.getMessage(), ex); + } + } + }; + } + }); + result = spel.getValue(context); + assertNotNull(result); + assertTrue(result.getClass().isArray()); + assertEquals(XYZ.X, Array.get(result, 0)); + assertEquals(XYZ.Y, Array.get(result, 1)); + assertEquals(XYZ.Z, Array.get(result, 2)); + } private static enum ABC {A, B, C}