Skip to content

Commit e4a9ade

Browse files
committed
Consistent SpelEvaluationException messages in findAccessorForMethod
Issue: SPR-16762 (cherry picked from commit 30363c8)
1 parent fbd8301 commit e4a9ade

File tree

4 files changed

+45
-39
lines changed

4 files changed

+45
-39
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -48,7 +48,7 @@ public SpelEvaluationException(int position, SpelMessage message, Object... inse
4848
}
4949

5050
public SpelEvaluationException(int position, Throwable cause, SpelMessage message, Object... inserts) {
51-
super(position, message.formatMessage(inserts),cause);
51+
super(position, message.formatMessage(inserts), cause);
5252
this.message = message;
5353
this.inserts = inserts;
5454
}

spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -45,13 +45,13 @@ public enum SpelMessage {
4545
"A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"),
4646

4747
METHOD_NOT_FOUND(Kind.ERROR, 1004,
48-
"Method call: Method {0} cannot be found on {1} type"),
48+
"Method call: Method {0} cannot be found on type {1}"),
4949

5050
TYPE_NOT_FOUND(Kind.ERROR, 1005,
5151
"Type cannot be found ''{0}''"),
5252

5353
FUNCTION_NOT_DEFINED(Kind.ERROR, 1006,
54-
"The function ''{0}'' could not be found"),
54+
"Function ''{0}'' could not be found"),
5555

5656
PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007,
5757
"Property or field ''{0}'' cannot be found on null"),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -22,7 +22,7 @@
2222
import org.springframework.util.ClassUtils;
2323

2424
/**
25-
* Utility methods (formatters, etc) used during parsing and evaluation.
25+
* Utility methods (formatters etc) used during parsing and evaluation.
2626
*
2727
* @author Andy Clement
2828
*/

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

Lines changed: 38 additions & 32 deletions
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-2018 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.
@@ -40,6 +40,7 @@
4040
import org.springframework.expression.spel.SpelMessage;
4141
import org.springframework.expression.spel.support.ReflectiveMethodExecutor;
4242
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
43+
import org.springframework.util.ObjectUtils;
4344

4445
/**
4546
* Expression language AST node that represents a method reference.
@@ -54,7 +55,7 @@ public class MethodReference extends SpelNodeImpl {
5455

5556
private final boolean nullSafe;
5657

57-
private String originalPrimitiveExitTypeDescriptor = null;
58+
private String originalPrimitiveExitTypeDescriptor;
5859

5960
private volatile CachedMethodExecutor cachedExecutor;
6061

@@ -126,7 +127,7 @@ private TypedValue getValueInternal(EvaluationContext evaluationContext,
126127
}
127128

128129
// either there was no accessor or it no longer existed
129-
executorToUse = findAccessorForMethod(this.name, argumentTypes, value, evaluationContext);
130+
executorToUse = findAccessorForMethod(argumentTypes, value, evaluationContext);
130131
this.cachedExecutor = new CachedMethodExecutor(
131132
executorToUse, (value instanceof Class ? (Class<?>) value : null), targetType, argumentTypes);
132133
try {
@@ -190,35 +191,40 @@ private MethodExecutor getCachedExecutor(EvaluationContext evaluationContext, Ob
190191
return null;
191192
}
192193

193-
private MethodExecutor findAccessorForMethod(String name, List<TypeDescriptor> argumentTypes,
194-
Object targetObject, EvaluationContext evaluationContext) throws SpelEvaluationException {
194+
private MethodExecutor findAccessorForMethod(List<TypeDescriptor> argumentTypes, Object targetObject,
195+
EvaluationContext evaluationContext) throws SpelEvaluationException {
195196

197+
AccessException accessException = null;
196198
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
197-
if (methodResolvers != null) {
198-
for (MethodResolver methodResolver : methodResolvers) {
199-
try {
200-
MethodExecutor methodExecutor = methodResolver.resolve(
201-
evaluationContext, targetObject, name, argumentTypes);
202-
if (methodExecutor != null) {
203-
return methodExecutor;
204-
}
205-
}
206-
catch (AccessException ex) {
207-
throw new SpelEvaluationException(getStartPosition(), ex,
208-
SpelMessage.PROBLEM_LOCATING_METHOD, name, targetObject.getClass());
199+
for (MethodResolver methodResolver : methodResolvers) {
200+
try {
201+
MethodExecutor methodExecutor = methodResolver.resolve(
202+
evaluationContext, targetObject, this.name, argumentTypes);
203+
if (methodExecutor != null) {
204+
return methodExecutor;
209205
}
210206
}
207+
catch (AccessException ex) {
208+
accessException = ex;
209+
break;
210+
}
211211
}
212212

213-
throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_NOT_FOUND,
214-
FormatHelper.formatMethodForMessage(name, argumentTypes),
215-
FormatHelper.formatClassNameForMessage(
216-
targetObject instanceof Class ? ((Class<?>) targetObject) : targetObject.getClass()));
213+
String method = FormatHelper.formatMethodForMessage(this.name, argumentTypes);
214+
String className = FormatHelper.formatClassNameForMessage(
215+
targetObject instanceof Class ? ((Class<?>) targetObject) : targetObject.getClass());
216+
if (accessException != null) {
217+
throw new SpelEvaluationException(
218+
getStartPosition(), accessException, SpelMessage.PROBLEM_LOCATING_METHOD, method, className);
219+
}
220+
else {
221+
throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_NOT_FOUND, method, className);
222+
}
217223
}
218224

219225
/**
220-
* Decode the AccessException, throwing a lightweight evaluation exception or, if the
221-
* cause was a RuntimeException, throw the RuntimeException directly.
226+
* Decode the AccessException, throwing a lightweight evaluation exception or,
227+
* if the cause was a RuntimeException, throw the RuntimeException directly.
222228
*/
223229
private void throwSimpleExceptionIfPossible(Object value, AccessException ex) {
224230
if (ex.getCause() instanceof InvocationTargetException) {
@@ -308,11 +314,11 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
308314
// Nothing on the stack but something is needed
309315
cf.loadTarget(mv);
310316
}
311-
if ((descriptor != null || !isStaticMethod) && nullSafe) {
317+
if ((descriptor != null || !isStaticMethod) && this.nullSafe) {
312318
mv.visitInsn(DUP);
313319
skipIfNull = new Label();
314320
Label continueLabel = new Label();
315-
mv.visitJumpInsn(IFNONNULL,continueLabel);
321+
mv.visitJumpInsn(IFNONNULL, continueLabel);
316322
CodeFlow.insertCheckCast(mv, this.exitTypeDescriptor);
317323
mv.visitJumpInsn(GOTO, skipIfNull);
318324
mv.visitLabel(continueLabel);
@@ -329,20 +335,19 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
329335
String classDesc = (Modifier.isPublic(method.getDeclaringClass().getModifiers()) ?
330336
method.getDeclaringClass().getName().replace('.', '/') :
331337
methodExecutor.getPublicDeclaringClass().getName().replace('.', '/'));
332-
if (!isStaticMethod) {
333-
if (descriptor == null || !descriptor.substring(1).equals(classDesc)) {
334-
CodeFlow.insertCheckCast(mv, "L" + classDesc);
335-
}
338+
if (!isStaticMethod && (descriptor == null || !descriptor.substring(1).equals(classDesc))) {
339+
CodeFlow.insertCheckCast(mv, "L" + classDesc);
336340
}
337341

338342
generateCodeForArguments(mv, cf, method, this.children);
339343
mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, method.getName(),
340344
CodeFlow.createSignatureDescriptor(method), method.getDeclaringClass().isInterface());
341345
cf.pushDescriptor(this.exitTypeDescriptor);
342-
if (originalPrimitiveExitTypeDescriptor != null) {
346+
347+
if (this.originalPrimitiveExitTypeDescriptor != null) {
343348
// The output of the accessor will be a primitive but from the block above it might be null,
344349
// so to have a 'common stack' element at skipIfNull target we need to box the primitive
345-
CodeFlow.insertBoxIfNecessary(mv, originalPrimitiveExitTypeDescriptor);
350+
CodeFlow.insertBoxIfNecessary(mv, this.originalPrimitiveExitTypeDescriptor);
346351
}
347352
if (skipIfNull != null) {
348353
mv.visitLabel(skipIfNull);
@@ -399,6 +404,7 @@ private static class CachedMethodExecutor {
399404

400405
public CachedMethodExecutor(MethodExecutor methodExecutor, Class<?> staticClass,
401406
TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
407+
402408
this.methodExecutor = methodExecutor;
403409
this.staticClass = staticClass;
404410
this.target = target;
@@ -407,7 +413,7 @@ public CachedMethodExecutor(MethodExecutor methodExecutor, Class<?> staticClass,
407413

408414
public boolean isSuitable(Object value, TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
409415
return ((this.staticClass == null || this.staticClass == value) &&
410-
this.target.equals(target) && this.argumentTypes.equals(argumentTypes));
416+
ObjectUtils.nullSafeEquals(this.target, target) && this.argumentTypes.equals(argumentTypes));
411417
}
412418

413419
public boolean hasProxyTarget() {

0 commit comments

Comments
 (0)