Skip to content

Commit 6f81cb6

Browse files
committed
Avoid unnecessary bridge method resolution around getMostSpecificMethod
Closes gh-35780
1 parent 566078b commit 6f81cb6

File tree

8 files changed

+69
-43
lines changed

8 files changed

+69
-43
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
566566
}
567567

568568
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
569-
Class<?> targetClass = clazz;
569+
Class<?> targetClass = ClassUtils.getUserClass(clazz);
570570

571571
do {
572572
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
@@ -586,12 +586,11 @@ private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
586586

587587
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
588588
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
589-
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
590-
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
589+
if (method.isBridge()) {
591590
return;
592591
}
593-
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
594-
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
592+
MergedAnnotation<?> ann = findAutowiredAnnotation(method);
593+
if (ann != null && method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
595594
if (Modifier.isStatic(method.getModifiers())) {
596595
if (logger.isInfoEnabled()) {
597596
logger.info("Autowired annotation is not supported on static methods: " + method);
@@ -609,7 +608,7 @@ private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
609608
}
610609
}
611610
boolean required = determineRequiredStatus(ann);
612-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
611+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
613612
methodElements.add(new AutowiredMethodElement(method, required, pd));
614613
}
615614
});

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.commons.logging.LogFactory;
2626

2727
import org.springframework.aop.support.AopUtils;
28+
import org.springframework.beans.factory.Aware;
2829
import org.springframework.core.MethodClassKey;
2930
import org.springframework.lang.Nullable;
3031
import org.springframework.util.ReflectionUtils;
@@ -97,6 +98,10 @@ private JCacheOperation<?> computeCacheOperation(Method method, @Nullable Class<
9798
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
9899
return null;
99100
}
101+
// Skip methods declared on BeanFactoryAware and co.
102+
if (Aware.class.isAssignableFrom(method.getDeclaringClass())) {
103+
return null;
104+
}
100105

101106
// The method may be on an interface, but we need metadata from the target class.
102107
// If the target class is null, the method will be unchanged.

spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.commons.logging.LogFactory;
2828

2929
import org.springframework.aop.support.AopUtils;
30+
import org.springframework.beans.factory.Aware;
3031
import org.springframework.core.MethodClassKey;
3132
import org.springframework.lang.Nullable;
3233
import org.springframework.util.ClassUtils;
@@ -139,6 +140,10 @@ private Collection<CacheOperation> computeCacheOperations(Method method, @Nullab
139140
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
140141
return null;
141142
}
143+
// Skip methods declared on BeanFactoryAware and co.
144+
if (Aware.class.isAssignableFrom(method.getDeclaringClass())) {
145+
return null;
146+
}
142147

143148
// The method may be on an interface, but we need metadata from the target class.
144149
// If the target class is null, the method will be unchanged.

spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
424424
}
425425

426426
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
427-
Class<?> targetClass = clazz;
427+
Class<?> targetClass = ClassUtils.getUserClass(clazz);
428428

429429
do {
430430
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
@@ -455,24 +455,23 @@ else if (javaxResourceType != null && field.isAnnotationPresent(javaxResourceTyp
455455
});
456456

457457
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
458-
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
459-
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
458+
if (method.isBridge()) {
460459
return;
461460
}
462-
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
463-
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
461+
if (ejbAnnotationType != null && method.isAnnotationPresent(ejbAnnotationType)) {
462+
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
464463
if (Modifier.isStatic(method.getModifiers())) {
465464
throw new IllegalStateException("@EJB annotation is not supported on static methods");
466465
}
467466
if (method.getParameterCount() != 1) {
468467
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
469468
}
470-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
471-
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
469+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
470+
currElements.add(new EjbRefElement(method, method, pd));
472471
}
473472
}
474-
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
475-
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
473+
else if (jakartaResourceType != null && method.isAnnotationPresent(jakartaResourceType)) {
474+
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
476475
if (Modifier.isStatic(method.getModifiers())) {
477476
throw new IllegalStateException("@Resource annotation is not supported on static methods");
478477
}
@@ -481,13 +480,13 @@ else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakart
481480
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
482481
}
483482
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
484-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
485-
currElements.add(new ResourceElement(method, bridgedMethod, pd));
483+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
484+
currElements.add(new ResourceElement(method, method, pd));
486485
}
487486
}
488487
}
489-
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
490-
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
488+
else if (javaxResourceType != null && method.isAnnotationPresent(javaxResourceType)) {
489+
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
491490
if (Modifier.isStatic(method.getModifiers())) {
492491
throw new IllegalStateException("@Resource annotation is not supported on static methods");
493492
}
@@ -496,8 +495,8 @@ else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxRes
496495
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
497496
}
498497
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
499-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
500-
currElements.add(new LegacyResourceElement(method, bridgedMethod, pd));
498+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
499+
currElements.add(new LegacyResourceElement(method, method, pd));
501500
}
502501
}
503502
}

spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ public static Method getMostSpecificMethod(Method bridgeMethod, @Nullable Class<
100100
}
101101

102102
private static Method resolveBridgeMethod(Method bridgeMethod, Class<?> targetClass) {
103-
boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass());
104103
Class<?> userClass = targetClass;
105-
if (!bridgeMethod.isBridge() && localBridge) {
104+
if (!bridgeMethod.isBridge()) {
106105
userClass = ClassUtils.getUserClass(targetClass);
107106
if (userClass == targetClass) {
108107
return bridgeMethod;
109108
}
110109
}
111110

111+
boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass());
112112
Object cacheKey = (localBridge ? bridgeMethod : new MethodClassKey(bridgeMethod, targetClass));
113113
Method bridgedMethod = cache.get(cacheKey);
114114
if (bridgedMethod == null) {
@@ -118,7 +118,7 @@ private static Method resolveBridgeMethod(Method bridgeMethod, Class<?> targetCl
118118
ReflectionUtils.doWithMethods(userClass, candidateMethods::add, filter);
119119
if (!candidateMethods.isEmpty()) {
120120
bridgedMethod = (candidateMethods.size() == 1 ? candidateMethods.get(0) :
121-
searchCandidates(candidateMethods, bridgeMethod));
121+
searchCandidates(candidateMethods, bridgeMethod, targetClass));
122122
}
123123
if (bridgedMethod == null) {
124124
// A bridge method was passed in but we couldn't find the bridged method.
@@ -149,14 +149,16 @@ private static boolean isBridgedCandidateFor(Method candidateMethod, Method brid
149149
* @return the bridged method, or {@code null} if none found
150150
*/
151151
@Nullable
152-
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
152+
private static Method searchCandidates(
153+
List<Method> candidateMethods, Method bridgeMethod, Class<?> targetClass) {
154+
153155
if (candidateMethods.isEmpty()) {
154156
return null;
155157
}
156158
Method previousMethod = null;
157159
boolean sameSig = true;
158160
for (Method candidateMethod : candidateMethods) {
159-
if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
161+
if (isBridgeMethodFor(bridgeMethod, candidateMethod, targetClass)) {
160162
return candidateMethod;
161163
}
162164
else if (previousMethod != null) {
@@ -172,12 +174,12 @@ else if (previousMethod != null) {
172174
* Determines whether the bridge {@link Method} is the bridge for the
173175
* supplied candidate {@link Method}.
174176
*/
175-
static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class<?> declaringClass) {
176-
if (isResolvedTypeMatch(candidateMethod, bridgeMethod, declaringClass)) {
177+
static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class<?> targetClass) {
178+
if (isResolvedTypeMatch(candidateMethod, bridgeMethod, targetClass)) {
177179
return true;
178180
}
179181
Method method = findGenericDeclaration(bridgeMethod);
180-
return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass));
182+
return (method != null && isResolvedTypeMatch(method, candidateMethod, targetClass));
181183
}
182184

183185
/**
@@ -186,14 +188,25 @@ static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Cl
186188
* are equal after resolving all types against the declaringType, otherwise
187189
* returns {@code false}.
188190
*/
189-
private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
191+
private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> targetClass) {
190192
Type[] genericParameters = genericMethod.getGenericParameterTypes();
191193
if (genericParameters.length != candidateMethod.getParameterCount()) {
192194
return false;
193195
}
196+
Class<?> clazz = targetClass;
197+
while (clazz != null && clazz != Object.class && clazz != genericMethod.getDeclaringClass()) {
198+
if (checkResolvedTypeMatch(genericMethod, candidateMethod, clazz)) {
199+
return true;
200+
}
201+
clazz = clazz.getSuperclass();
202+
}
203+
return false;
204+
}
205+
206+
private static boolean checkResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> clazz) {
194207
Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
195208
for (int i = 0; i < candidateParameters.length; i++) {
196-
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, declaringClass);
209+
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz);
197210
Class<?> candidateParameter = candidateParameters[i];
198211
if (candidateParameter.isArray()) {
199212
// An array type: compare the component type.
@@ -273,7 +286,9 @@ private static Method searchForMatch(Class<?> type, Method bridgeMethod) {
273286
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
274287
* JDK-6342411</a>.
275288
* @return whether signatures match as described
289+
* @deprecated as of 6.2.13: not necessary anymore due to {@link #getMostSpecificMethod}
276290
*/
291+
@Deprecated(since = "6.2.13", forRemoval = true)
277292
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
278293
if (bridgeMethod == bridgedMethod) {
279294
// Same method: for common purposes, return true to proceed as if it was a visibility bridge.

spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -429,14 +429,14 @@ public interface Adder<T> {
429429
}
430430

431431

432-
public abstract static class AbstractDateAdder implements Adder<Date> {
432+
public abstract static class AbstractAdder<T extends Serializable> implements Adder<T> {
433433

434434
@Override
435-
public abstract void add(Date date);
435+
public abstract void add(T item);
436436
}
437437

438438

439-
public static class DateAdder extends AbstractDateAdder {
439+
public static class DateAdder extends AbstractAdder<Date> {
440440

441441
@Override
442442
public void add(Date date) {

spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.io.Serializable;
2121
import java.lang.reflect.AnnotatedElement;
2222
import java.lang.reflect.Member;
23-
import java.lang.reflect.Method;
2423
import java.lang.reflect.Modifier;
2524
import java.util.ArrayList;
2625
import java.util.Arrays;
@@ -429,7 +428,7 @@ private InjectionMetadata buildPersistenceMetadata(Class<?> clazz) {
429428
}
430429

431430
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
432-
Class<?> targetClass = clazz;
431+
Class<?> targetClass = ClassUtils.getUserClass(clazz);
433432

434433
do {
435434
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
@@ -445,21 +444,20 @@ private InjectionMetadata buildPersistenceMetadata(Class<?> clazz) {
445444
});
446445

447446
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
448-
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
449-
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
447+
if (method.isBridge()) {
450448
return;
451449
}
452-
if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) ||
453-
bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) &&
454-
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
450+
if ((method.isAnnotationPresent(PersistenceContext.class) ||
451+
method.isAnnotationPresent(PersistenceUnit.class)) &&
452+
method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
455453
if (Modifier.isStatic(method.getModifiers())) {
456454
throw new IllegalStateException("Persistence annotations are not supported on static methods");
457455
}
458456
if (method.getParameterCount() != 1) {
459457
throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
460458
}
461-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
462-
currElements.add(new PersistenceElement(method, bridgedMethod, pd));
459+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
460+
currElements.add(new PersistenceElement(method, method, pd));
463461
}
464462
});
465463

spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.commons.logging.LogFactory;
2626

2727
import org.springframework.aop.support.AopUtils;
28+
import org.springframework.beans.factory.Aware;
2829
import org.springframework.context.EmbeddedValueResolverAware;
2930
import org.springframework.core.MethodClassKey;
3031
import org.springframework.lang.Nullable;
@@ -166,6 +167,10 @@ protected TransactionAttribute computeTransactionAttribute(Method method, @Nulla
166167
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
167168
return null;
168169
}
170+
// Skip methods declared on BeanFactoryAware and co.
171+
if (Aware.class.isAssignableFrom(method.getDeclaringClass())) {
172+
return null;
173+
}
169174

170175
// The method may be on an interface, but we need attributes from the target class.
171176
// If the target class is null, the method will be unchanged.

0 commit comments

Comments
 (0)