1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2024 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.
50
50
*/
51
51
public final class BridgeMethodResolver {
52
52
53
- private static final Map <Method , Method > cache = new ConcurrentReferenceHashMap <>();
53
+ private static final Map <Object , Method > cache = new ConcurrentReferenceHashMap <>();
54
54
55
55
private BridgeMethodResolver () {
56
56
}
57
57
58
58
59
59
/**
60
- * Find the original method for the supplied {@link Method bridge Method}.
60
+ * Find the local original method for the supplied {@link Method bridge Method}.
61
61
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
62
62
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
63
63
* Callers are <strong>not</strong> required to check for bridging before calling this method.
64
- * @param bridgeMethod the method to introspect
64
+ * @param bridgeMethod the method to introspect against its declaring class
65
65
* @return the original method (either the bridged method or the passed-in method
66
66
* if no more specific one could be found)
67
+ * @see #getMostSpecificMethod(Method, Class)
67
68
*/
68
69
public static Method findBridgedMethod (Method bridgeMethod ) {
69
- if (!bridgeMethod .isBridge ()) {
70
+ return resolveBridgeMethod (bridgeMethod , bridgeMethod .getDeclaringClass ());
71
+ }
72
+
73
+ /**
74
+ * Determine the most specific method for the supplied {@link Method bridge Method}
75
+ * in the given class hierarchy, even if not available on the local declaring class.
76
+ * <p>This is effectively a combination of {@link ClassUtils#getMostSpecificMethod}
77
+ * and {@link #findBridgedMethod}, resolving the original method even if no bridge
78
+ * method has been generated at the same class hierarchy level (a known difference
79
+ * between the Eclipse compiler and regular javac).
80
+ * @param bridgeMethod the method to introspect against the given target class
81
+ * @param targetClass the target class to find methods on
82
+ * @return the original method (either the bridged method or the passed-in method
83
+ * if no more specific one could be found)
84
+ * @since 6.1.3
85
+ * @see #findBridgedMethod
86
+ * @see org.springframework.util.ClassUtils#getMostSpecificMethod
87
+ */
88
+ public static Method getMostSpecificMethod (Method bridgeMethod , @ Nullable Class <?> targetClass ) {
89
+ Method specificMethod = ClassUtils .getMostSpecificMethod (bridgeMethod , targetClass );
90
+ return resolveBridgeMethod (specificMethod ,
91
+ (targetClass != null ? targetClass : specificMethod .getDeclaringClass ()));
92
+ }
93
+
94
+ private static Method resolveBridgeMethod (Method bridgeMethod , Class <?> targetClass ) {
95
+ boolean localBridge = (targetClass == bridgeMethod .getDeclaringClass ());
96
+ if (!bridgeMethod .isBridge () && localBridge ) {
70
97
return bridgeMethod ;
71
98
}
72
- Method bridgedMethod = cache .get (bridgeMethod );
99
+
100
+ Object cacheKey = (localBridge ? bridgeMethod : new MethodClassKey (bridgeMethod , targetClass ));
101
+ Method bridgedMethod = cache .get (cacheKey );
73
102
if (bridgedMethod == null ) {
74
103
// Gather all methods with matching name and parameter size.
75
104
List <Method > candidateMethods = new ArrayList <>();
76
- MethodFilter filter = candidateMethod ->
77
- isBridgedCandidateFor (candidateMethod , bridgeMethod );
78
- ReflectionUtils .doWithMethods (bridgeMethod .getDeclaringClass (), candidateMethods ::add , filter );
105
+ MethodFilter filter = (candidateMethod -> isBridgedCandidateFor (candidateMethod , bridgeMethod ));
106
+ ReflectionUtils .doWithMethods (targetClass , candidateMethods ::add , filter );
79
107
if (!candidateMethods .isEmpty ()) {
80
- bridgedMethod = candidateMethods .size () == 1 ?
81
- candidateMethods .get (0 ) :
82
- searchCandidates (candidateMethods , bridgeMethod );
108
+ bridgedMethod = (candidateMethods .size () == 1 ? candidateMethods .get (0 ) :
109
+ searchCandidates (candidateMethods , bridgeMethod , targetClass ));
83
110
}
84
111
if (bridgedMethod == null ) {
85
112
// A bridge method was passed in but we couldn't find the bridged method.
86
113
// Let's proceed with the passed-in method and hope for the best...
87
114
bridgedMethod = bridgeMethod ;
88
115
}
89
- cache .put (bridgeMethod , bridgedMethod );
116
+ cache .put (cacheKey , bridgedMethod );
90
117
}
91
118
return bridgedMethod ;
92
119
}
@@ -110,19 +137,19 @@ private static boolean isBridgedCandidateFor(Method candidateMethod, Method brid
110
137
* @return the bridged method, or {@code null} if none found
111
138
*/
112
139
@ Nullable
113
- private static Method searchCandidates (List <Method > candidateMethods , Method bridgeMethod ) {
140
+ private static Method searchCandidates (List <Method > candidateMethods , Method bridgeMethod , Class <?> targetClass ) {
114
141
if (candidateMethods .isEmpty ()) {
115
142
return null ;
116
143
}
117
144
Method previousMethod = null ;
118
145
boolean sameSig = true ;
119
146
for (Method candidateMethod : candidateMethods ) {
120
- if (isBridgeMethodFor (bridgeMethod , candidateMethod , bridgeMethod . getDeclaringClass () )) {
147
+ if (isBridgeMethodFor (bridgeMethod , candidateMethod , targetClass )) {
121
148
return candidateMethod ;
122
149
}
123
150
else if (previousMethod != null ) {
124
- sameSig = sameSig &&
125
- Arrays . equals ( candidateMethod .getGenericParameterTypes (), previousMethod .getGenericParameterTypes ());
151
+ sameSig = sameSig && Arrays . equals (
152
+ candidateMethod .getGenericParameterTypes (), previousMethod .getGenericParameterTypes ());
126
153
}
127
154
previousMethod = candidateMethod ;
128
155
}
@@ -133,12 +160,12 @@ else if (previousMethod != null) {
133
160
* Determines whether the bridge {@link Method} is the bridge for the
134
161
* supplied candidate {@link Method}.
135
162
*/
136
- static boolean isBridgeMethodFor (Method bridgeMethod , Method candidateMethod , Class <?> declaringClass ) {
137
- if (isResolvedTypeMatch (candidateMethod , bridgeMethod , declaringClass )) {
163
+ static boolean isBridgeMethodFor (Method bridgeMethod , Method candidateMethod , Class <?> targetClass ) {
164
+ if (isResolvedTypeMatch (candidateMethod , bridgeMethod , targetClass )) {
138
165
return true ;
139
166
}
140
167
Method method = findGenericDeclaration (bridgeMethod );
141
- return (method != null && isResolvedTypeMatch (method , candidateMethod , declaringClass ));
168
+ return (method != null && isResolvedTypeMatch (method , candidateMethod , targetClass ));
142
169
}
143
170
144
171
/**
@@ -147,14 +174,14 @@ static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Cl
147
174
* are equal after resolving all types against the declaringType, otherwise
148
175
* returns {@code false}.
149
176
*/
150
- private static boolean isResolvedTypeMatch (Method genericMethod , Method candidateMethod , Class <?> declaringClass ) {
177
+ private static boolean isResolvedTypeMatch (Method genericMethod , Method candidateMethod , Class <?> targetClass ) {
151
178
Type [] genericParameters = genericMethod .getGenericParameterTypes ();
152
179
if (genericParameters .length != candidateMethod .getParameterCount ()) {
153
180
return false ;
154
181
}
155
182
Class <?>[] candidateParameters = candidateMethod .getParameterTypes ();
156
183
for (int i = 0 ; i < candidateParameters .length ; i ++) {
157
- ResolvableType genericParameter = ResolvableType .forMethodParameter (genericMethod , i , declaringClass );
184
+ ResolvableType genericParameter = ResolvableType .forMethodParameter (genericMethod , i , targetClass );
158
185
Class <?> candidateParameter = candidateParameters [i ];
159
186
if (candidateParameter .isArray ()) {
160
187
// An array type: compare the component type.
@@ -163,7 +190,8 @@ private static boolean isResolvedTypeMatch(Method genericMethod, Method candidat
163
190
}
164
191
}
165
192
// A non-array type: compare the type itself.
166
- if (!ClassUtils .resolvePrimitiveIfNecessary (candidateParameter ).equals (ClassUtils .resolvePrimitiveIfNecessary (genericParameter .toClass ()))) {
193
+ if (!ClassUtils .resolvePrimitiveIfNecessary (candidateParameter ).equals (
194
+ ClassUtils .resolvePrimitiveIfNecessary (genericParameter .toClass ()))) {
167
195
return false ;
168
196
}
169
197
}
@@ -177,6 +205,10 @@ private static boolean isResolvedTypeMatch(Method genericMethod, Method candidat
177
205
*/
178
206
@ Nullable
179
207
private static Method findGenericDeclaration (Method bridgeMethod ) {
208
+ if (!bridgeMethod .isBridge ()) {
209
+ return bridgeMethod ;
210
+ }
211
+
180
212
// Search parent types for method that has same signature as bridge.
181
213
Class <?> superclass = bridgeMethod .getDeclaringClass ().getSuperclass ();
182
214
while (superclass != null && Object .class != superclass ) {
0 commit comments