Skip to content

Commit

Permalink
Cache BridgeMethodResolver results
Browse files Browse the repository at this point in the history
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
  • Loading branch information
philwebb authored and jhoeller committed Mar 22, 2019
1 parent a57d6ba commit db8eaec
Showing 1 changed file with 23 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
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
Expand All @@ -47,6 +50,8 @@
*/
public final class BridgeMethodResolver {

private static final Map<Method, Method> cache = new ConcurrentReferenceHashMap<>();

private BridgeMethodResolver() {
}

Expand All @@ -64,32 +69,26 @@ public static Method findBridgedMethod(Method bridgeMethod) {
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;
}

/**
Expand Down

0 comments on commit db8eaec

Please sign in to comment.