diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 53e0b21daa7..9abc44ce572 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -566,7 +566,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } final List elements = new ArrayList<>(); - Class targetClass = clazz; + Class targetClass = ClassUtils.getUserClass(clazz); do { final List fieldElements = new ArrayList<>(); @@ -586,12 +586,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA final List methodElements = new ArrayList<>(); ReflectionUtils.doWithLocalMethods(targetClass, method -> { - Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); - if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { + if (method.isBridge()) { return; } - MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod); - if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + MergedAnnotation ann = findAutowiredAnnotation(method); + if (ann != null && method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); @@ -609,7 +608,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } } boolean required = determineRequiredStatus(ann); - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz); methodElements.add(new AutowiredMethodElement(method, required, pd)); } }); diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java index f89275c1735..ada4c5ecc3d 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.Aware; import org.springframework.core.MethodClassKey; import org.springframework.lang.Nullable; import org.springframework.util.ReflectionUtils; @@ -97,6 +98,10 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } + // Skip methods declared on BeanFactoryAware and co. + if (Aware.class.isAssignableFrom(method.getDeclaringClass())) { + return null; + } // The method may be on an interface, but we need metadata from the target class. // If the target class is null, the method will be unchanged. diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java index afa39bf07d8..0db22a2eea0 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.Aware; import org.springframework.core.MethodClassKey; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -139,6 +140,10 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } + // Skip methods declared on BeanFactoryAware and co. + if (Aware.class.isAssignableFrom(method.getDeclaringClass())) { + return null; + } // The method may be on an interface, but we need metadata from the target class. // If the target class is null, the method will be unchanged. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java index adc8502814f..d306de8881b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java @@ -424,7 +424,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean } List elements = new ArrayList<>(); - Class targetClass = clazz; + Class targetClass = ClassUtils.getUserClass(clazz); do { final List currElements = new ArrayList<>(); @@ -455,24 +455,23 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { - Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); - if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { + if (method.isBridge()) { return; } - if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) { - if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + if (ejbAnnotationType != null && method.isAnnotationPresent(ejbAnnotationType)) { + if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@EJB annotation is not supported on static methods"); } if (method.getParameterCount() != 1) { throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method); } - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); - currElements.add(new EjbRefElement(method, bridgedMethod, pd)); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz); + currElements.add(new EjbRefElement(method, method, pd)); } } - else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) { - if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + else if (jakartaResourceType != null && method.isAnnotationPresent(jakartaResourceType)) { + if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static methods"); } @@ -481,13 +480,13 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method); } if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) { - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); - currElements.add(new ResourceElement(method, bridgedMethod, pd)); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz); + currElements.add(new ResourceElement(method, method, pd)); } } } - else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) { - if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + else if (javaxResourceType != null && method.isAnnotationPresent(javaxResourceType)) { + if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static methods"); } @@ -496,8 +495,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method); } if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) { - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); - currElements.add(new LegacyResourceElement(method, bridgedMethod, pd)); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz); + currElements.add(new LegacyResourceElement(method, method, pd)); } } } 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 4da0a1d2b8f..af1fcab26a3 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -100,15 +100,15 @@ public final class BridgeMethodResolver { } private static Method resolveBridgeMethod(Method bridgeMethod, Class targetClass) { - boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass()); Class userClass = targetClass; - if (!bridgeMethod.isBridge() && localBridge) { + if (!bridgeMethod.isBridge()) { userClass = ClassUtils.getUserClass(targetClass); if (userClass == targetClass) { return bridgeMethod; } } + boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass()); Object cacheKey = (localBridge ? bridgeMethod : new MethodClassKey(bridgeMethod, targetClass)); Method bridgedMethod = cache.get(cacheKey); if (bridgedMethod == null) { @@ -118,7 +118,7 @@ public final class BridgeMethodResolver { ReflectionUtils.doWithMethods(userClass, candidateMethods::add, filter); if (!candidateMethods.isEmpty()) { bridgedMethod = (candidateMethods.size() == 1 ? candidateMethods.get(0) : - searchCandidates(candidateMethods, bridgeMethod)); + searchCandidates(candidateMethods, bridgeMethod, targetClass)); } if (bridgedMethod == null) { // A bridge method was passed in but we couldn't find the bridged method. @@ -149,14 +149,16 @@ public final class BridgeMethodResolver { * @return the bridged method, or {@code null} if none found */ @Nullable - private static Method searchCandidates(List candidateMethods, Method bridgeMethod) { + private static Method searchCandidates( + List candidateMethods, Method bridgeMethod, Class targetClass) { + if (candidateMethods.isEmpty()) { return null; } Method previousMethod = null; boolean sameSig = true; for (Method candidateMethod : candidateMethods) { - if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) { + if (isBridgeMethodFor(bridgeMethod, candidateMethod, targetClass)) { return candidateMethod; } else if (previousMethod != null) { @@ -172,12 +174,12 @@ public final class BridgeMethodResolver { * Determines whether the bridge {@link Method} is the bridge for the * supplied candidate {@link Method}. */ - static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class declaringClass) { - if (isResolvedTypeMatch(candidateMethod, bridgeMethod, declaringClass)) { + static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class targetClass) { + if (isResolvedTypeMatch(candidateMethod, bridgeMethod, targetClass)) { return true; } Method method = findGenericDeclaration(bridgeMethod); - return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass)); + return (method != null && isResolvedTypeMatch(method, candidateMethod, targetClass)); } /** @@ -186,14 +188,25 @@ public final class BridgeMethodResolver { * are equal after resolving all types against the declaringType, otherwise * returns {@code false}. */ - private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class declaringClass) { + private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class targetClass) { Type[] genericParameters = genericMethod.getGenericParameterTypes(); if (genericParameters.length != candidateMethod.getParameterCount()) { return false; } + Class clazz = targetClass; + while (clazz != null && clazz != Object.class && clazz != genericMethod.getDeclaringClass()) { + if (checkResolvedTypeMatch(genericMethod, candidateMethod, clazz)) { + return true; + } + clazz = clazz.getSuperclass(); + } + return false; + } + + private static boolean checkResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class clazz) { Class[] candidateParameters = candidateMethod.getParameterTypes(); for (int i = 0; i < candidateParameters.length; i++) { - ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, declaringClass); + ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz); Class candidateParameter = candidateParameters[i]; if (candidateParameter.isArray()) { // An array type: compare the component type. @@ -273,7 +286,9 @@ public final class BridgeMethodResolver { * introduced in Java 6 to fix * JDK-6342411. * @return whether signatures match as described + * @deprecated as of 6.2.13: not necessary anymore due to {@link #getMostSpecificMethod} */ + @Deprecated(since = "6.2.13", forRemoval = true) public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) { if (bridgeMethod == bridgedMethod) { // Same method: for common purposes, return true to proceed as if it was a visibility bridge. diff --git a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java b/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java index 7741c115afa..3f9d55e416b 100644 --- a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java @@ -429,14 +429,14 @@ class BridgeMethodResolverTests { } - public abstract static class AbstractDateAdder implements Adder { + public abstract static class AbstractAdder implements Adder { @Override - public abstract void add(Date date); + public abstract void add(T item); } - public static class DateAdder extends AbstractDateAdder { + public static class DateAdder extends AbstractAdder { @Override public void add(Date date) { diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index 950fb2f0a94..decd5470489 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -20,7 +20,6 @@ import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -429,7 +428,7 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar } List elements = new ArrayList<>(); - Class targetClass = clazz; + Class targetClass = ClassUtils.getUserClass(clazz); do { final List currElements = new ArrayList<>(); @@ -445,21 +444,20 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { - Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); - if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { + if (method.isBridge()) { return; } - if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) || - bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) && - method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { + if ((method.isAnnotationPresent(PersistenceContext.class) || + method.isAnnotationPresent(PersistenceUnit.class)) && + method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("Persistence annotations are not supported on static methods"); } if (method.getParameterCount() != 1) { throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method); } - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); - currElements.add(new PersistenceElement(method, bridgedMethod, pd)); + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz); + currElements.add(new PersistenceElement(method, method, pd)); } }); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java index 33dbcb836ab..f6288470167 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.Aware; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.MethodClassKey; import org.springframework.lang.Nullable; @@ -166,6 +167,10 @@ public abstract class AbstractFallbackTransactionAttributeSource if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } + // Skip methods declared on BeanFactoryAware and co. + if (Aware.class.isAssignableFrom(method.getDeclaringClass())) { + return null; + } // The method may be on an interface, but we need attributes from the target class. // If the target class is null, the method will be unchanged.