Skip to content

Commit 348eb91

Browse files
committed
ReflectiveMethodResolver applies useDistance mode by default (with fine-tuned varargs handling)
Issue: SPR-12803 Issue: SPR-12808
1 parent a64532e commit 348eb91

File tree

6 files changed

+311
-371
lines changed

6 files changed

+311
-371
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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,13 +40,14 @@
4040
public class ReflectionHelper {
4141

4242
/**
43-
* Compare argument arrays and return information about whether they match. A supplied
44-
* type converter and conversionAllowed flag allow for matches to take into account
45-
* that a type may be transformed into a different type by the converter.
46-
* @param expectedArgTypes the array of types the method/constructor is expecting
47-
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
43+
* Compare argument arrays and return information about whether they match.
44+
* A supplied type converter and conversionAllowed flag allow for matches to take
45+
* into account that a type may be transformed into a different type by the converter.
46+
* @param expectedArgTypes the types the method/constructor is expecting
47+
* @param suppliedArgTypes the types that are being supplied at the point of invocation
4848
* @param typeConverter a registered type converter
49-
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
49+
* @return a MatchInfo object indicating what kind of match it was,
50+
* or {@code null} if it was not a match
5051
*/
5152
static ArgumentsMatchInfo compareArguments(
5253
List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
@@ -90,7 +91,7 @@ public static int getTypeDifferenceWeight(List<TypeDescriptor> paramTypes, List<
9091
int result = 0;
9192
for (int i = 0; i < paramTypes.size(); i++) {
9293
TypeDescriptor paramType = paramTypes.get(i);
93-
TypeDescriptor argType = argTypes.get(i);
94+
TypeDescriptor argType = (i < argTypes.size() ? argTypes.get(i) : null);
9495
if (argType == null) {
9596
if (paramType.isPrimitive()) {
9697
return Integer.MAX_VALUE;
@@ -127,13 +128,15 @@ else if (ClassUtils.isAssignable(paramTypeClazz, superClass)) {
127128
}
128129

129130
/**
130-
* Compare argument arrays and return information about whether they match. A supplied type converter and
131-
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
132-
* type by the converter. This variant of compareArguments also allows for a varargs match.
133-
* @param expectedArgTypes the array of types the method/constructor is expecting
134-
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
131+
* Compare argument arrays and return information about whether they match.
132+
* A supplied type converter and conversionAllowed flag allow for matches to
133+
* take into account that a type may be transformed into a different type by the
134+
* converter. This variant of compareArguments also allows for a varargs match.
135+
* @param expectedArgTypes the types the method/constructor is expecting
136+
* @param suppliedArgTypes the types that are being supplied at the point of invocation
135137
* @param typeConverter a registered type converter
136-
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
138+
* @return a MatchInfo object indicating what kind of match it was,
139+
* or {@code null} if it was not a match
137140
*/
138141
static ArgumentsMatchInfo compareArgumentsVarargs(
139142
List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
@@ -246,12 +249,14 @@ public static boolean convertAllArguments(TypeConverter converter, Object[] argu
246249
* @param converter the type converter to use for attempting conversions
247250
* @param arguments the actual arguments that need conversion
248251
* @param methodOrCtor the target Method or Constructor
249-
* @param varargsPosition the known position of the varargs argument, if any (null if not varargs)
250-
* @return true if some kind of conversion occurred on an argument
252+
* @param varargsPosition the known position of the varargs argument, if any
253+
* ({@code null} if not varargs)
254+
* @return {@code true} if some kind of conversion occurred on an argument
251255
* @throws EvaluationException if a problem occurs during conversion
252256
*/
253257
static boolean convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor,
254258
Integer varargsPosition) throws EvaluationException {
259+
255260
boolean conversionOccurred = false;
256261
if (varargsPosition == null) {
257262
for (int i = 0; i < arguments.length; i++) {
@@ -320,9 +325,9 @@ private static boolean isFirstEntryInArray(Object value, Object possibleArray) {
320325

321326
/**
322327
* Package up the arguments so that they correctly match what is expected in parameterTypes.
323-
* For example, if parameterTypes is (int, String[]) because the second parameter was declared String...
324-
* then if arguments is [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}] in order to
325-
* match the expected parameterTypes.
328+
* For example, if parameterTypes is {@code (int, String[])} because the second parameter
329+
* was declared {@code String...}, then if arguments is {@code [1,"a","b"]} then it must be
330+
* repackaged as {@code [1,new String[]{"a","b"}]} in order to match the expected types.
326331
* @param requiredParameterTypes the types of the parameters for the invocation
327332
* @param args the arguments to be setup ready for the invocation
328333
* @return a repackaged array of arguments where any varargs setup has been done
@@ -374,10 +379,11 @@ static enum ArgumentsMatchKind {
374379

375380

376381
/**
377-
* An instance of ArgumentsMatchInfo describes what kind of match was achieved between two sets of arguments -
378-
* the set that a method/constructor is expecting and the set that are being supplied at the point of invocation.
379-
* If the kind indicates that conversion is required for some of the arguments then the arguments that require
380-
* conversion are listed in the argsRequiringConversion array.
382+
* An instance of ArgumentsMatchInfo describes what kind of match was achieved
383+
* between two sets of arguments - the set that a method/constructor is expecting
384+
* and the set that are being supplied at the point of invocation. If the kind
385+
* indicates that conversion is required for some of the arguments then the arguments
386+
* that require conversion are listed in the argsRequiringConversion array.
381387
*/
382388
static class ArgumentsMatchInfo {
383389

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -62,17 +62,18 @@ public class ReflectiveMethodResolver implements MethodResolver {
6262

6363

6464
public ReflectiveMethodResolver() {
65-
this.useDistance = false;
65+
this.useDistance = true;
6666
}
6767

6868
/**
69-
* This constructors allows the ReflectiveMethodResolver to be configured such that it will
70-
* use a distance computation to check which is the better of two close matches (when there
71-
* are multiple matches). Using the distance computation is intended to ensure matches
72-
* are more closely representative of what a Java compiler would do when taking into
73-
* account boxing/unboxing and whether the method candidates are declared to handle a
74-
* supertype of the type (of the argument) being passed in.
75-
* @param useDistance true if distance computation should be used when calculating matches
69+
* This constructor allows the ReflectiveMethodResolver to be configured such that it
70+
* will use a distance computation to check which is the better of two close matches
71+
* (when there are multiple matches). Using the distance computation is intended to
72+
* ensure matches are more closely representative of what a Java compiler would do
73+
* when taking into account boxing/unboxing and whether the method candidates are
74+
* declared to handle a supertype of the type (of the argument) being passed in.
75+
* @param useDistance {@code true} if distance computation should be used when
76+
* calculating matches; {@code false} otherwise
7677
*/
7778
public ReflectiveMethodResolver(boolean useDistance) {
7879
this.useDistance = useDistance;
@@ -175,17 +176,17 @@ else if (paramTypes.length == argumentTypes.size()) {
175176
return new ReflectiveMethodExecutor(method);
176177
}
177178
else if (matchInfo.isCloseMatch()) {
178-
if (!this.useDistance) {
179-
// Take this as a close match if there isn't one already
180-
if (closeMatch == null) {
179+
if (this.useDistance) {
180+
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
181+
if (closeMatch == null || matchDistance < closeMatchDistance) {
182+
// This is a better match...
181183
closeMatch = method;
184+
closeMatchDistance = matchDistance;
182185
}
183186
}
184187
else {
185-
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
186-
if (matchDistance < closeMatchDistance) {
187-
// This is a better match...
188-
closeMatchDistance = matchDistance;
188+
// Take this as a close match if there isn't one already
189+
if (closeMatch == null) {
189190
closeMatch = method;
190191
}
191192
}

0 commit comments

Comments
 (0)