我正在尝试创建一个小型实用程序来代替我在整个项目中使用反射(主要是为了使用 LambdaMetafactory 的性能优势),但我在创建 CallSite 时遇到了麻烦。但是,仅在访问不是我自己的类时才会出现此问题。访问 3rd 方库甚至 Java 自己的类(例如 java.lang.Object)将导致 NoClassDefFoundError 不是针对 3rd 方类,而是针对我的接口。
public final class Accessor {
private static Constructor<MethodHandles.Lookup> lookupConstructor;
static {
newLookupConstructor();
}
protected static void newLookupConstructor() {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("MethodHandles.Lookup class constructor (Class) not found! Check java version.");
}
}
private Accessor() { }
public static <T> T to(Class<T> interfaze, Class<?> clazz, String method, Class<?>... params) {
try {
return to(interfaze, clazz.getDeclaredMethod(method, params));
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
public static <T> T to(Class<T> interfaze, Method method) {
try {
MethodHandles.Lookup caller = lookupConstructor.newInstance(method.getDeclaringClass());
MethodHandle implMethod = caller.unreflect(method);
CallSite site = LambdaMetafactory.metafactory(caller, method.getName(), MethodType.methodType(interfaze), implMethod.type(), implMethod, implMethod.type());
// ^ java.lang.NoClassDefFoundError for the passed interfaze class
return (T) site.getTarget().invoke();
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
我运行的单元测试证明了这个问题可以在这里找到:
final class AccessorTest {
@Test // SUCCESS
@DisplayName("Verify MethodHandles.Lookup constructor")
void lookupConstructorAvailabilityTest() {
Assertions.assertDoesNotThrow(() -> Accessor.newLookupConstructor());
}
@Test // SUCCESS
@DisplayName("Verify available matching instance method is called")
void findMatchingMethodAndCallTest() {
ObjectAccessor accessor = Accessor.to(ObjectAccessor.class, TestObject.class, "instanceMethod");
Assertions.assertNotNull(accessor);
Assertions.assertTrue(accessor.instanceMethod(new TestObject()));
}
@Test // SUCCESS
@DisplayName("Verify available matching static method is called")
void findMatchingStaticMethodAndCallTest() {
ObjectAccessor accessor = Accessor.to(ObjectAccessor.class, TestObject.class, "staticMethod");
Assertions.assertNotNull(accessor);
Assertions.assertTrue(accessor.staticMethod());
}
@Test // FAILURE
@DisplayName("Verify java.lang.Object#toString works")
void testDynamicToStringInvokation() {
ToString accessor = Accessor.to(ToString.class, Object.class, "toString");
// ^ java.lang.NoClassDefFoundError: com/gmail/justisroot/autoecon/data/AccessorTest$ToString
Assertions.assertNotNull(accessor);
Assertions.assertEquals(accessor.toString(Integer.valueOf(42)), "42");
}
public interface ObjectAccessor {
public boolean instanceMethod(TestObject o);
public boolean staticMethod();
}
public interface ToString {
public String toString(Object o);
}
}
这将抛出以下内容:
java.lang.NoClassDefFoundError: com/gmail/justisroot/autoecon/data/AccessorTest$ToString at java.base/jdk.internal.misc.Unsafe.defineAnonymousClass0(Native Method) at java.base/jdk.internal.misc.Unsafe。在 java.base/java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:188) 的 java.base/java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:320) 的 defineAnonymousClass(Unsafe.java:1223)在 java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:317) 在 com.gmail.justisroot.autoecon.data.Accessor.to(Accessor.java:43)
我已经花了太多时间绞尽脑汁寻找解决方案。第二双眼睛肯定会有所帮助。
我究竟做错了什么?