当我在ByteBuddy生成的类中创建自定义构造函数时出现异常

问题描述 投票:0回答:1

我使用ByteBuddy实现一个包装类,为现有的域对象提供索引。

public interface Indexed {
    Long getIndex();
}

这是我的域类。

public interface Alpha {
    String getName();
    Long getRating();
}

public static class AlphaImpl implements Alpha {
    private final String name;
    private final Long rating;

    public AlphaImpl(String name, Long rating) {
        this.name = name;
        this.rating = rating;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Long getRating() {
        return rating;
    }
}

当我生成只有默认构造函数的类并通过setter传递域对象和索引时,一切正常。

@Test
public void test1 () throws Exception {
    Class<?> klass = new ByteBuddy()
            .subclass(Alpha.class)
            .implement(Indexed.class)

            .defineProperty("origin", Alpha.class)

            .method(ElementMatchers.isOverriddenFrom(Alpha.class))
            .intercept(MethodCall.invokeSelf().onField("origin"))

            .defineProperty("index", Long.class)

            .make()
            .load(AlphaImpl.class.getClassLoader())
            .getLoaded();

    Alpha origin = new AlphaImpl("Wrapped object", 10L);
    Alpha indexed = (Alpha) klass.getConstructor().newInstance();

    klass.getDeclaredMethod("setOrigin", Alpha.class).invoke(indexed, origin);
    klass.getDeclaredMethod("setIndex", Long.class).invoke(indexed, 13L);

    assertEquals("Wrapped object", indexed.getName());
    assertEquals(10L, (long)indexed.getRating());
    assertEquals(13L, (long)((Indexed)indexed).getIndex());
}

但是,当我将属性设置为只读并定义构造函数来设置它们时,我有一个例外。

@Test
public void test2 () throws Exception {
    Class<?> klass = new ByteBuddy()
            .subclass(Alpha.class, ConstructorStrategy.Default.NO_CONSTRUCTORS
            )
            .implement(Indexed.class)

            .defineConstructor(Visibility.PUBLIC)
            .withParameters(Alpha.class, Long.class)
            .intercept(
                    FieldAccessor.ofField("origin").setsArgumentAt(0)
                    .andThen(FieldAccessor.ofField("index").setsArgumentAt(1)))

            .defineProperty("origin", Alpha.class, true)

            .method(ElementMatchers.isOverriddenFrom(Alpha.class))
            .intercept(MethodCall.invokeSelf().onField("origin"))

            .defineProperty("index", Long.class, true)

            .make()
            .load(AlphaImpl.class.getClassLoader())
            .getLoaded();

    Alpha origin = new AlphaImpl("Wrapped object", 10L);
    Alpha indexed = (Alpha) klass.getConstructor(Alpha.class, Long.class).newInstance(origin, 13L);

    assertEquals("Wrapped object", indexed.getName());
    assertEquals(10L, (long)indexed.getRating());
    assertEquals(13L, (long)((Indexed)indexed).getIndex());
}

结果stacktrace如下。

Constructor must call super() or this() before return
Exception Details:
  Location:
    IndexedWrapperTest$Alpha$ByteBuddy$V9ew7nId.<init>(LIndexedWrapperTest$Alpha;Ljava/lang/Long;)V @10: return
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: 2a2b b500 102a 2cb5 001b b1            

java.lang.VerifyError: Constructor must call super() or this() before return
Exception Details:
  Location:
    IndexedWrapperTest$Alpha$ByteBuddy$V9ew7nId.<init>(LIndexedWrapperTest$Alpha;Ljava/lang/Long;)V @10: return
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: 2a2b b500 102a 2cb5 001b b1            

    at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3138)
    at java.base/java.lang.Class.getConstructor0(Class.java:3343)
    at java.base/java.lang.Class.getConstructor(Class.java:2152)
    at IndexedWrapperTest.test2(IndexedWrapperTest.java:95)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:106)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:66)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:117)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:155)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:137)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.base/java.lang.Thread.run(Thread.java:834)

我该如何解决这个问题?

如何以最小的开销检查结果类是一个简单的组合?

java byte-buddy
1个回答
0
投票

我自己找到了解决方案。

我必须显式调用Objects默认构造函数,如下面的清单所示。在介绍MethodCall.invoke(Object.class.getConstructor())之后,一切正常。

@Test
public void test2 () throws Exception {
    Class<?> klass = new ByteBuddy()
            .subclass(Alpha.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
            .implement(Indexed.class)

            .defineConstructor(Visibility.PUBLIC)
            .withParameters(Alpha.class, Long.class)
            .intercept(

                    // Invoke Objects default constructor explicitly
                    MethodCall.invoke(Object.class.getConstructor())

                    .andThen(FieldAccessor.ofField("origin").setsArgumentAt(0))
                    .andThen(FieldAccessor.ofField("index").setsArgumentAt(1)))

            .defineProperty("origin", Alpha.class, true)

            .method(ElementMatchers.anyOf(Alpha.class.getMethods()))
            .intercept(MethodCall.invokeSelf().onField("origin").withAllArguments())

            .defineProperty("index", Long.class, true)

            .make()
            .load(AlphaImpl.class.getClassLoader())
            .getLoaded();

    Alpha origin = new AlphaImpl("Wrapped object", 10L);
    Alpha indexed = (Alpha) klass.getConstructor(Alpha.class, Long.class).newInstance(origin, 13L);

    assertEquals("Wrapped object", indexed.getName());
    assertEquals(10L, (long)indexed.getRating());
    assertEquals(13L, (long)((Indexed)indexed).getIndex());
}
© www.soinside.com 2019 - 2024. All rights reserved.