From 4300fec023adb9ec12349337de6a2add77578f46 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Mar 2024 23:48:26 +0100 Subject: [PATCH] Restore ability to return original method at ClassUtils level as well Closes gh-32365 --- .../springframework/aop/support/AopUtils.java | 6 ++--- .../org/springframework/util/ClassUtils.java | 27 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java index 6b30d707a16d..526a3a0032c0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java @@ -194,10 +194,10 @@ public static boolean isFinalizeMethod(@Nullable Method method) { * this method resolves bridge methods in order to retrieve attributes from * the original method definition. * @param method the method to be invoked, which may come from an interface - * @param targetClass the target class for the current invocation. - * May be {@code null} or may not even implement the method. + * @param targetClass the target class for the current invocation + * (can be {@code null} or may not even implement the method) * @return the specific target method, or the original method if the - * {@code targetClass} doesn't implement it or is {@code null} + * {@code targetClass} does not implement it * @see org.springframework.util.ClassUtils#getMostSpecificMethod * @see org.springframework.core.BridgeMethodResolver#getMostSpecificMethod */ diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 3218838cac60..8e68e6af839b 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -256,7 +256,7 @@ public static ClassLoader overrideThreadContextClassLoader(@Nullable ClassLoader * style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State"). * @param name the name of the Class * @param classLoader the class loader to use - * (may be {@code null}, which indicates the default class loader) + * (can be {@code null}, which indicates the default class loader) * @return a class instance for the supplied name * @throws ClassNotFoundException if the class was not found * @throws LinkageError if the class file could not be loaded @@ -328,7 +328,7 @@ public static Class forName(String name, @Nullable ClassLoader classLoader) * the exceptions thrown in case of class loading failure. * @param className the name of the Class * @param classLoader the class loader to use - * (may be {@code null}, which indicates the default class loader) + * (can be {@code null}, which indicates the default class loader) * @return a class instance for the supplied name * @throws IllegalArgumentException if the class name was not resolvable * (that is, the class could not be found or the class file could not be loaded) @@ -362,7 +362,7 @@ public static Class resolveClassName(String className, @Nullable ClassLoader * one of its dependencies is not present or cannot be loaded. * @param className the name of the class to check * @param classLoader the class loader to use - * (may be {@code null} which indicates the default class loader) + * (can be {@code null} which indicates the default class loader) * @return whether the specified class is present (including all of its * superclasses and interfaces) * @throws IllegalStateException if the corresponding class is resolvable but @@ -389,7 +389,7 @@ public static boolean isPresent(String className, @Nullable ClassLoader classLoa * Check whether the given class is visible in the given ClassLoader. * @param clazz the class to check (typically an interface) * @param classLoader the ClassLoader to check against - * (may be {@code null} in which case this method will always return {@code true}) + * (can be {@code null} in which case this method will always return {@code true}) */ public static boolean isVisible(Class clazz, @Nullable ClassLoader classLoader) { if (classLoader == null) { @@ -413,7 +413,7 @@ public static boolean isVisible(Class clazz, @Nullable ClassLoader classLoade * i.e. whether it is loaded by the given ClassLoader or a parent of it. * @param clazz the class to analyze * @param classLoader the ClassLoader to potentially cache metadata in - * (may be {@code null} which indicates the system class loader) + * (can be {@code null} which indicates the system class loader) */ public static boolean isCacheSafe(Class clazz, @Nullable ClassLoader classLoader) { Assert.notNull(clazz, "Class must not be null"); @@ -727,7 +727,7 @@ public static String classNamesToString(Class... classes) { * in the given collection. *

Basically like {@code AbstractCollection.toString()}, but stripping * the "class "/"interface " prefix before every class name. - * @param classes a Collection of Class objects (may be {@code null}) + * @param classes a Collection of Class objects (can be {@code null}) * @return a String of form "[com.foo.Bar, com.foo.Baz]" * @see java.util.AbstractCollection#toString() */ @@ -782,7 +782,7 @@ public static Class[] getAllInterfacesForClass(Class clazz) { *

If the class itself is an interface, it gets returned as sole interface. * @param clazz the class to analyze for interfaces * @param classLoader the ClassLoader that the interfaces need to be visible in - * (may be {@code null} when accepting all declared interfaces) + * (can be {@code null} when accepting all declared interfaces) * @return all interfaces that the given object implements as an array */ public static Class[] getAllInterfacesForClass(Class clazz, @Nullable ClassLoader classLoader) { @@ -817,7 +817,7 @@ public static Set> getAllInterfacesForClassAsSet(Class clazz) { *

If the class itself is an interface, it gets returned as sole interface. * @param clazz the class to analyze for interfaces * @param classLoader the ClassLoader that the interfaces need to be visible in - * (may be {@code null} when accepting all declared interfaces) + * (can be {@code null} when accepting all declared interfaces) * @return all interfaces that the given object implements as a Set */ public static Set> getAllInterfacesForClassAsSet(Class clazz, @Nullable ClassLoader classLoader) { @@ -1146,7 +1146,7 @@ public static String getQualifiedMethodName(Method method) { * fully qualified interface/class name + "." + method name. * @param method the method * @param clazz the clazz that the method is being invoked on - * (may be {@code null} to indicate the method's declaring class) + * (can be {@code null} to indicate the method's declaring class) * @return the qualified name of the method * @since 4.3.4 */ @@ -1227,7 +1227,7 @@ public static boolean hasMethod(Class clazz, String methodName, Class... p * @param clazz the clazz to analyze * @param methodName the name of the method * @param paramTypes the parameter types of the method - * (may be {@code null} to indicate any signature) + * (can be {@code null} to indicate any signature) * @return the method (never {@code null}) * @throws IllegalStateException if the method has not been found * @see Class#getMethod @@ -1266,7 +1266,7 @@ else if (candidates.isEmpty()) { * @param clazz the clazz to analyze * @param methodName the name of the method * @param paramTypes the parameter types of the method - * (may be {@code null} to indicate any signature) + * (can be {@code null} to indicate any signature) * @return the method, or {@code null} if not found * @see Class#getMethod */ @@ -1355,13 +1355,14 @@ public static boolean hasAtLeastOneMethodWithName(Class clazz, String methodN * implementation will fall back to returning the originally provided method. * @param method the method to be invoked, which may come from an interface * @param targetClass the target class for the current invocation - * (may be {@code null} or may not even implement the method) + * (can be {@code null} or may not even implement the method) * @return the specific target method, or the original method if the * {@code targetClass} does not implement it * @see #getInterfaceMethodIfPossible(Method, Class) */ public static Method getMostSpecificMethod(Method method, @Nullable Class targetClass) { - if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) { + if (targetClass != null && targetClass != method.getDeclaringClass() && + (isOverridable(method, targetClass) || !method.getDeclaringClass().isAssignableFrom(targetClass))) { try { if (Modifier.isPublic(method.getModifiers())) { try {