From db8eaec1315f445746331d2f7c9f66cbf33e8911 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 8 Mar 2019 17:16:56 -0800 Subject: [PATCH] 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 --- .../core/BridgeMethodResolver.java | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index 36a1e3288220..254564068983 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -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 @@ -47,6 +50,8 @@ */ public final class BridgeMethodResolver { + private static final Map cache = new ConcurrentReferenceHashMap<>(); + private BridgeMethodResolver() { } @@ -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 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 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; } /**