Browse Source

Avoid unnecessary bridge method resolution around getMostSpecificMethod

Closes gh-35780
pull/35814/head
Juergen Hoeller 1 month ago
parent
commit
6f81cb625c
  1. 11
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
  2. 5
      spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java
  3. 5
      spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
  4. 29
      spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
  5. 35
      spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java
  6. 6
      spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java
  7. 16
      spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java
  8. 5
      spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java

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

@ -566,7 +566,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA @@ -566,7 +566,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
Class<?> targetClass = ClassUtils.getUserClass(clazz);
do {
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
@ -586,12 +586,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA @@ -586,12 +586,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
final List<InjectionMetadata.InjectedElement> 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 @@ -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));
}
});

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

@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; @@ -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 @@ -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.

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

@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; @@ -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 @@ -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.

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

@ -424,7 +424,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean @@ -424,7 +424,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
Class<?> targetClass = ClassUtils.getUserClass(clazz);
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
@ -455,24 +455,23 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean @@ -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 @@ -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 @@ -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));
}
}
}

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

@ -100,15 +100,15 @@ public final class BridgeMethodResolver { @@ -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 { @@ -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 { @@ -149,14 +149,16 @@ public final class BridgeMethodResolver {
* @return the bridged method, or {@code null} if none found
*/
@Nullable
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
private static Method searchCandidates(
List<Method> 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 { @@ -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 { @@ -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 { @@ -273,7 +286,9 @@ public final class BridgeMethodResolver {
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
* JDK-6342411</a>.
* @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.

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

@ -429,14 +429,14 @@ class BridgeMethodResolverTests { @@ -429,14 +429,14 @@ class BridgeMethodResolverTests {
}
public abstract static class AbstractDateAdder implements Adder<Date> {
public abstract static class AbstractAdder<T extends Serializable> implements Adder<T> {
@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<Date> {
@Override
public void add(Date date) {

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

@ -20,7 +20,6 @@ import java.beans.PropertyDescriptor; @@ -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 @@ -429,7 +428,7 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
Class<?> targetClass = ClassUtils.getUserClass(clazz);
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
@ -445,21 +444,20 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar @@ -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));
}
});

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

@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; @@ -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 @@ -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.

Loading…
Cancel
Save