Revise getPubliclyAccessibleMethodIfPossible to rely on Module#isExported
Backport Bot / build (push) Waiting to run
Details
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:24], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
Deploy Docs / Dispatch docs deployment (push) Waiting to run
Details
Backport Bot / build (push) Waiting to run
Details
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:24], map[id:ubuntu-latest name:Linux]) (push) Waiting to run
Details
Deploy Docs / Dispatch docs deployment (push) Waiting to run
Details
This avoids reflection and cache access for regular public and exported types. Closes gh-35556
This commit is contained in:
parent
e3da26ebbd
commit
a6f6ecfe6c
|
@ -1483,8 +1483,8 @@ public abstract class ClassUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the highest publicly accessible method in the supplied method's type hierarchy that
|
||||
* has a method signature equivalent to the supplied method, if possible.
|
||||
* Get the closest publicly accessible (and exported) method in the supplied method's type
|
||||
* hierarchy that has a method signature equivalent to the supplied method, if possible.
|
||||
* <p>Otherwise, this method recursively searches the class hierarchy and implemented
|
||||
* interfaces for an equivalent method that is {@code public} and declared in a
|
||||
* {@code public} type.
|
||||
|
@ -1507,18 +1507,21 @@ public abstract class ClassUtils {
|
|||
* @see #getMostSpecificMethod(Method, Class)
|
||||
*/
|
||||
public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
|
||||
// If the method is not public, we can abort the search immediately.
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
// If the method is not public or its declaring class is public and exported already,
|
||||
// we can abort the search immediately (avoiding reflection as well as cache access).
|
||||
if (!Modifier.isPublic(method.getModifiers()) || (Modifier.isPublic(declaringClass.getModifiers()) &&
|
||||
declaringClass.getModule().isExported(declaringClass.getPackageName(), ClassUtils.class.getModule()))) {
|
||||
return method;
|
||||
}
|
||||
|
||||
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass, true);
|
||||
// If we found a method in a public interface, return the interface method.
|
||||
if (interfaceMethod != method) {
|
||||
if (interfaceMethod != method && interfaceMethod.getDeclaringClass().getModule().isExported(
|
||||
interfaceMethod.getDeclaringClass().getPackageName(), ClassUtils.class.getModule())) {
|
||||
return interfaceMethod;
|
||||
}
|
||||
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
// Bypass cache for java.lang.Object unless it is actually an overridable method declared there.
|
||||
if (declaringClass.getSuperclass() == Object.class && !ReflectionUtils.isObjectMethod(method)) {
|
||||
return method;
|
||||
|
@ -1540,7 +1543,9 @@ public abstract class ClassUtils {
|
|||
if (method == null) {
|
||||
break;
|
||||
}
|
||||
if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
if (Modifier.isPublic(method.getDeclaringClass().getModifiers()) &&
|
||||
method.getDeclaringClass().getModule().isExported(
|
||||
method.getDeclaringClass().getPackageName(), ClassUtils.class.getModule())) {
|
||||
result = method;
|
||||
}
|
||||
current = method.getDeclaringClass().getSuperclass();
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.lang.reflect.Member;
|
|||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.URLConnection;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -687,13 +688,13 @@ class ClassUtilsTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void publicMethodInObjectClass() throws Exception {
|
||||
void publicMethodInPublicClass() throws Exception {
|
||||
Class<?> originalType = String.class;
|
||||
Method originalMethod = originalType.getDeclaredMethod("hashCode");
|
||||
Method originalMethod = originalType.getDeclaredMethod("toString");
|
||||
|
||||
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(Object.class);
|
||||
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("hashCode");
|
||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
|
||||
assertThat(publiclyAccessibleMethod).isSameAs(originalMethod);
|
||||
assertPubliclyAccessible(publiclyAccessibleMethod);
|
||||
}
|
||||
|
||||
|
@ -703,9 +704,20 @@ class ClassUtilsTests {
|
|||
Method originalMethod = originalType.getDeclaredMethod("size");
|
||||
|
||||
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
||||
// Should find the interface method in List.
|
||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(List.class);
|
||||
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("size");
|
||||
// Should not find the interface method in List.
|
||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
|
||||
assertThat(publiclyAccessibleMethod).isSameAs(originalMethod);
|
||||
assertPubliclyAccessible(publiclyAccessibleMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicMethodInNonExportedClass() throws Exception {
|
||||
Class<?> originalType = getClass().getClassLoader().loadClass("sun.net.www.protocol.http.HttpURLConnection");
|
||||
Method originalMethod = originalType.getDeclaredMethod("getOutputStream");
|
||||
|
||||
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(URLConnection.class);
|
||||
assertThat(publiclyAccessibleMethod.getName()).isSameAs(originalMethod.getName());
|
||||
assertPubliclyAccessible(publiclyAccessibleMethod);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue