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
|
* Get the closest publicly accessible (and exported) method in the supplied method's type
|
||||||
* has a method signature equivalent to the supplied method, if possible.
|
* hierarchy that has a method signature equivalent to the supplied method, if possible.
|
||||||
* <p>Otherwise, this method recursively searches the class hierarchy and implemented
|
* <p>Otherwise, this method recursively searches the class hierarchy and implemented
|
||||||
* interfaces for an equivalent method that is {@code public} and declared in a
|
* interfaces for an equivalent method that is {@code public} and declared in a
|
||||||
* {@code public} type.
|
* {@code public} type.
|
||||||
|
@ -1507,18 +1507,21 @@ public abstract class ClassUtils {
|
||||||
* @see #getMostSpecificMethod(Method, Class)
|
* @see #getMostSpecificMethod(Method, Class)
|
||||||
*/
|
*/
|
||||||
public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
|
public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
|
||||||
// If the method is not public, we can abort the search immediately.
|
Class<?> declaringClass = method.getDeclaringClass();
|
||||||
if (!Modifier.isPublic(method.getModifiers())) {
|
// 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;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass, true);
|
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass, true);
|
||||||
// If we found a method in a public interface, return the interface method.
|
// 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;
|
return interfaceMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> declaringClass = method.getDeclaringClass();
|
|
||||||
// Bypass cache for java.lang.Object unless it is actually an overridable method declared there.
|
// Bypass cache for java.lang.Object unless it is actually an overridable method declared there.
|
||||||
if (declaringClass.getSuperclass() == Object.class && !ReflectionUtils.isObjectMethod(method)) {
|
if (declaringClass.getSuperclass() == Object.class && !ReflectionUtils.isObjectMethod(method)) {
|
||||||
return method;
|
return method;
|
||||||
|
@ -1540,7 +1543,9 @@ public abstract class ClassUtils {
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
break;
|
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;
|
result = method;
|
||||||
}
|
}
|
||||||
current = method.getDeclaringClass().getSuperclass();
|
current = method.getDeclaringClass().getSuperclass();
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -687,13 +688,13 @@ class ClassUtilsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void publicMethodInObjectClass() throws Exception {
|
void publicMethodInPublicClass() throws Exception {
|
||||||
Class<?> originalType = String.class;
|
Class<?> originalType = String.class;
|
||||||
Method originalMethod = originalType.getDeclaredMethod("hashCode");
|
Method originalMethod = originalType.getDeclaredMethod("toString");
|
||||||
|
|
||||||
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
||||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(Object.class);
|
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
|
||||||
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("hashCode");
|
assertThat(publiclyAccessibleMethod).isSameAs(originalMethod);
|
||||||
assertPubliclyAccessible(publiclyAccessibleMethod);
|
assertPubliclyAccessible(publiclyAccessibleMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,9 +704,20 @@ class ClassUtilsTests {
|
||||||
Method originalMethod = originalType.getDeclaredMethod("size");
|
Method originalMethod = originalType.getDeclaredMethod("size");
|
||||||
|
|
||||||
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
|
||||||
// Should find the interface method in List.
|
// Should not find the interface method in List.
|
||||||
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(List.class);
|
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
|
||||||
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("size");
|
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);
|
assertPubliclyAccessible(publiclyAccessibleMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue