Skip to content

Commit ba774c6

Browse files
committed
Restore behavior for ClassUtils.getInterfaceMethodIfPossible()
Commit 47f88e1 introduced support for invoking init/destroy/SpEL methods via public types whenever possible. To achieve that, getInterfaceMethodIfPossible() was modified so that it only returned interface methods from public interfaces; however, we have learned that third parties relied on the previous behavior which found any interface method (even in non-public interfaces). In light of the above, this commit partially reverts commit 47f88e1 in order to reinstate getInterfaceMethodIfPossible() in non-deprecated form with its previous behavior. See gh-33216
1 parent d2ea5b4 commit ba774c6

File tree

2 files changed

+247
-75
lines changed

2 files changed

+247
-75
lines changed

spring-core/src/main/java/org/springframework/util/ClassUtils.java

+26-11
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,18 @@ public abstract class ClassUtils {
134134
*/
135135
private static final Set<Class<?>> javaLanguageInterfaces;
136136

137+
/**
138+
* Cache for equivalent methods on a interface implemented by the declaring class.
139+
* <p>A {@code null} value signals that no interface method was found for the key.
140+
*/
141+
private static final Map<Method, Method> interfaceMethodCache = new ConcurrentReferenceHashMap<>(256);
142+
137143
/**
138144
* Cache for equivalent methods on a public interface implemented by the declaring class.
139145
* <p>A {@code null} value signals that no public interface method was found for the key.
146+
* @since 6.2
140147
*/
141-
private static final Map<Method, Method> interfaceMethodCache = new ConcurrentReferenceHashMap<>(256);
148+
private static final Map<Method, Method> publicInterfaceMethodCache = new ConcurrentReferenceHashMap<>(256);
142149

143150
/**
144151
* Cache for equivalent public methods in a public declaring type within the type hierarchy
@@ -1403,7 +1410,8 @@ public static Method getMostSpecificMethod(Method method, @Nullable Class<?> tar
14031410
* @param method the method to be invoked, potentially from an implementation class
14041411
* @return the corresponding interface method, or the original method if none found
14051412
* @since 5.1
1406-
* @deprecated in favor of {@link #getPubliclyAccessibleMethodIfPossible(Method, Class)}
1413+
* @see #getPubliclyAccessibleMethodIfPossible(Method, Class)
1414+
* @deprecated in favor of {@link #getInterfaceMethodIfPossible(Method, Class)}
14071415
*/
14081416
@Deprecated
14091417
public static Method getInterfaceMethodIfPossible(Method method) {
@@ -1421,38 +1429,45 @@ public static Method getInterfaceMethodIfPossible(Method method) {
14211429
* @since 5.3.16
14221430
* @see #getPubliclyAccessibleMethodIfPossible(Method, Class)
14231431
* @see #getMostSpecificMethod
1424-
* @deprecated in favor of {@link #getPubliclyAccessibleMethodIfPossible(Method, Class)}
14251432
*/
1426-
@Deprecated(since = "6.2")
14271433
public static Method getInterfaceMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
1434+
return getInterfaceMethodIfPossible(method, targetClass, false);
1435+
}
1436+
1437+
private static Method getInterfaceMethodIfPossible(Method method, @Nullable Class<?> targetClass,
1438+
boolean requirePublicInterface) {
1439+
14281440
Class<?> declaringClass = method.getDeclaringClass();
1429-
if (!Modifier.isPublic(method.getModifiers()) || declaringClass.isInterface()) {
1441+
if (!Modifier.isPublic(method.getModifiers()) || (declaringClass.isInterface() &&
1442+
(!requirePublicInterface || Modifier.isPublic(declaringClass.getModifiers())))) {
14301443
return method;
14311444
}
14321445
String methodName = method.getName();
14331446
Class<?>[] parameterTypes = method.getParameterTypes();
14341447

1448+
Map<Method, Method> methodCache = (requirePublicInterface ? publicInterfaceMethodCache : interfaceMethodCache);
14351449
// Try cached version of method in its declaring class
1436-
Method result = interfaceMethodCache.computeIfAbsent(method,
1437-
key -> findInterfaceMethodIfPossible(methodName, parameterTypes, declaringClass, Object.class));
1450+
Method result = methodCache.computeIfAbsent(method, key -> findInterfaceMethodIfPossible(
1451+
methodName, parameterTypes, declaringClass, Object.class, requirePublicInterface));
14381452
if (result == null && targetClass != null) {
14391453
// No interface method found yet -> try given target class (possibly a subclass of the
14401454
// declaring class, late-binding a base class method to a subclass-declared interface:
14411455
// see e.g. HashMap.HashIterator.hasNext)
1442-
result = findInterfaceMethodIfPossible(methodName, parameterTypes, targetClass, declaringClass);
1456+
result = findInterfaceMethodIfPossible(
1457+
methodName, parameterTypes, targetClass, declaringClass, requirePublicInterface);
14431458
}
14441459
return (result != null ? result : method);
14451460
}
14461461

14471462
@Nullable
14481463
private static Method findInterfaceMethodIfPossible(String methodName, Class<?>[] parameterTypes,
1449-
Class<?> startClass, Class<?> endClass) {
1464+
Class<?> startClass, Class<?> endClass, boolean requirePublicInterface) {
14501465

14511466
Class<?> current = startClass;
14521467
while (current != null && current != endClass) {
14531468
for (Class<?> ifc : current.getInterfaces()) {
14541469
try {
1455-
if (Modifier.isPublic(ifc.getModifiers())) {
1470+
if (!requirePublicInterface || Modifier.isPublic(ifc.getModifiers())) {
14561471
return ifc.getMethod(methodName, parameterTypes);
14571472
}
14581473
}
@@ -1500,7 +1515,7 @@ public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nulla
15001515
return method;
15011516
}
15021517

1503-
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass);
1518+
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass, true);
15041519
// If we found a method in a public interface, return the interface method.
15051520
if (!interfaceMethod.equals(method)) {
15061521
return interfaceMethod;

0 commit comments

Comments
 (0)