Cache BridgeMethodResolver results

Add a cache to `BridgeMethodResolver` to help with repeated calls to
resolve the same methods. Since bridge method resolution can be somewhat
expensive (especially when resolving generics), and the number of bridge
methods is quite small, a cache generally helps.

This commit also simplifies the code a little by calling `doWithMethods`
directly rather than relying on `ReflectionUtils.getAllDeclaredMethods`
to do so. The methods list is now always created, but we save the list
creation that `getAllDeclaredMethods` used to do.

Closes gh-22579
This commit is contained in:
Phillip Webb 2019-03-08 17:16:56 -08:00 committed by Juergen Hoeller
parent a57d6ba5f3
commit db8eaec131
1 changed files with 23 additions and 24 deletions

View File

@ -21,10 +21,13 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
@ -47,6 +50,8 @@ import org.springframework.util.ReflectionUtils;
*/
public final class BridgeMethodResolver {
private static final Map<Method, Method> cache = new ConcurrentReferenceHashMap<>();
private BridgeMethodResolver() {
}
@ -64,32 +69,26 @@ public final class BridgeMethodResolver {
if (!bridgeMethod.isBridge()) {
return bridgeMethod;
}
// Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<>();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
for (Method candidateMethod : methods) {
if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
candidateMethods.add(candidateMethod);
Method bridgedMethod = cache.get(bridgeMethod);
if (bridgedMethod == null) {
// Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<>();
MethodFilter filter = candidateMethod ->
isBridgedCandidateFor(candidateMethod, bridgeMethod);
ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
if (!candidateMethods.isEmpty()) {
bridgedMethod = candidateMethods.size() == 1 ?
candidateMethods.get(0) :
searchCandidates(candidateMethods, bridgeMethod);
}
if (bridgedMethod == null) {
// A bridge method was passed in but we couldn't find the bridged method.
// Let's proceed with the passed-in method and hope for the best...
bridgedMethod = bridgeMethod;
}
cache.put(bridgeMethod, bridgedMethod);
}
// Now perform simple quick check.
if (candidateMethods.size() == 1) {
return candidateMethods.get(0);
}
// Search for candidate match.
Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
if (bridgedMethod != null) {
// Bridged method found...
return bridgedMethod;
}
else {
// A bridge method was passed in but we couldn't find the bridged method.
// Let's proceed with the passed-in method and hope for the best...
return bridgeMethod;
}
return bridgedMethod;
}
/**