1
1
/*
2
- * Copyright 2002-2015 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
40
40
import org .springframework .expression .spel .SpelMessage ;
41
41
import org .springframework .expression .spel .support .ReflectiveMethodExecutor ;
42
42
import org .springframework .expression .spel .support .ReflectiveMethodResolver ;
43
+ import org .springframework .util .ObjectUtils ;
43
44
44
45
/**
45
46
* Expression language AST node that represents a method reference.
@@ -54,7 +55,7 @@ public class MethodReference extends SpelNodeImpl {
54
55
55
56
private final boolean nullSafe ;
56
57
57
- private String originalPrimitiveExitTypeDescriptor = null ;
58
+ private String originalPrimitiveExitTypeDescriptor ;
58
59
59
60
private volatile CachedMethodExecutor cachedExecutor ;
60
61
@@ -126,7 +127,7 @@ private TypedValue getValueInternal(EvaluationContext evaluationContext,
126
127
}
127
128
128
129
// either there was no accessor or it no longer existed
129
- executorToUse = findAccessorForMethod (this . name , argumentTypes , value , evaluationContext );
130
+ executorToUse = findAccessorForMethod (argumentTypes , value , evaluationContext );
130
131
this .cachedExecutor = new CachedMethodExecutor (
131
132
executorToUse , (value instanceof Class ? (Class <?>) value : null ), targetType , argumentTypes );
132
133
try {
@@ -190,35 +191,40 @@ private MethodExecutor getCachedExecutor(EvaluationContext evaluationContext, Ob
190
191
return null ;
191
192
}
192
193
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 {
195
196
197
+ AccessException accessException = null ;
196
198
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 ;
209
205
}
210
206
}
207
+ catch (AccessException ex ) {
208
+ accessException = ex ;
209
+ break ;
210
+ }
211
211
}
212
212
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
+ }
217
223
}
218
224
219
225
/**
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.
222
228
*/
223
229
private void throwSimpleExceptionIfPossible (Object value , AccessException ex ) {
224
230
if (ex .getCause () instanceof InvocationTargetException ) {
@@ -308,11 +314,11 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
308
314
// Nothing on the stack but something is needed
309
315
cf .loadTarget (mv );
310
316
}
311
- if ((descriptor != null || !isStaticMethod ) && nullSafe ) {
317
+ if ((descriptor != null || !isStaticMethod ) && this . nullSafe ) {
312
318
mv .visitInsn (DUP );
313
319
skipIfNull = new Label ();
314
320
Label continueLabel = new Label ();
315
- mv .visitJumpInsn (IFNONNULL ,continueLabel );
321
+ mv .visitJumpInsn (IFNONNULL , continueLabel );
316
322
CodeFlow .insertCheckCast (mv , this .exitTypeDescriptor );
317
323
mv .visitJumpInsn (GOTO , skipIfNull );
318
324
mv .visitLabel (continueLabel );
@@ -329,20 +335,19 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
329
335
String classDesc = (Modifier .isPublic (method .getDeclaringClass ().getModifiers ()) ?
330
336
method .getDeclaringClass ().getName ().replace ('.' , '/' ) :
331
337
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 );
336
340
}
337
341
338
342
generateCodeForArguments (mv , cf , method , this .children );
339
343
mv .visitMethodInsn ((isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL ), classDesc , method .getName (),
340
344
CodeFlow .createSignatureDescriptor (method ), method .getDeclaringClass ().isInterface ());
341
345
cf .pushDescriptor (this .exitTypeDescriptor );
342
- if (originalPrimitiveExitTypeDescriptor != null ) {
346
+
347
+ if (this .originalPrimitiveExitTypeDescriptor != null ) {
343
348
// The output of the accessor will be a primitive but from the block above it might be null,
344
349
// 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 );
346
351
}
347
352
if (skipIfNull != null ) {
348
353
mv .visitLabel (skipIfNull );
@@ -399,6 +404,7 @@ private static class CachedMethodExecutor {
399
404
400
405
public CachedMethodExecutor (MethodExecutor methodExecutor , Class <?> staticClass ,
401
406
TypeDescriptor target , List <TypeDescriptor > argumentTypes ) {
407
+
402
408
this .methodExecutor = methodExecutor ;
403
409
this .staticClass = staticClass ;
404
410
this .target = target ;
@@ -407,7 +413,7 @@ public CachedMethodExecutor(MethodExecutor methodExecutor, Class<?> staticClass,
407
413
408
414
public boolean isSuitable (Object value , TypeDescriptor target , List <TypeDescriptor > argumentTypes ) {
409
415
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 ));
411
417
}
412
418
413
419
public boolean hasProxyTarget () {
0 commit comments