Byte Buddy: java.lang.AbstractMethodError: error when invoking method

Byte Buddy: java.lang.AbstractMethodError: error when invoking method

编辑

原题中的实际界面和示例域存在缺陷。特此正确举例。

import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
import static net.bytebuddy.matcher.ElementMatchers.isSetter;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;

public class Example {

    public interface Domain {
        int getIdentifier();
    }

    public class Person implements Domain {

        private int personId;
        private String firstName;
        private String lastName;

        @Override
        public int getIdentifier() {
            return personId;
        }

        public int getPersonId() {
            return personId;
        }

        public void setPersonId(final int personId) {
            this.personId = personId;
        }

        // further  getters and setters ommitted

    }

    public static void main(final String[] args) throws InstantiationException, IllegalAccessException {
        final Domain domain = new ByteBuddy()
                .subclass(Domain.class)
                .name("Person")
                .method(isGetter().or(isSetter())).intercept(FieldAccessor.ofBeanProperty())
                .defineField("personId", int.class, Visibility.PRIVATE)           
                .defineField("firstName", String.class, Visibility.PRIVATE)           
                .defineField("lastName", String.class, Visibility.PRIVATE)           
                .method(isDeclaredBy(Domain.class))
                  .intercept(FieldAccessor.ofField("personId"))
                .make()
                .load(Example.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded()
                .newInstance();

        System.out.println(domain.getIdentifier());     

    }
}

Byte Buddy 代码是否生成如上述代码所述的 Person class? (我该如何检查?)

预编辑

我正在尝试在运行时生成一些使用 Byte Buddy API ( http://bytebuddy.net/#/ )

实现域接口的 classes

应用程序从外部源获取一些输入,我想将该输入(如 [=46= 的名称]、某些变量的名称)转换为实现接口的 POJO。在下面的示例代码中,输入由域名字符串数组表示。

成功生成代码后,我想使用那些在运行时生成的 classes 来映射文件(例如解析行并将它们映射到生成的 POJO)。

以下代码 (clazz.setIdentifier(1);) 抛出 Exception in thread "main" java.lang.AbstractMethodError: Person.setIdentifier(I)V

public class Sample {

    public interface Domain {
        void setIdentifier(int i);
        int getIdentifier();
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        String[] domainNames = {"Person", "City", "Country"};   
        for (String domainName : domainNames) {
            Class<?> domain = new ByteBuddy()
                    .subclass(Domain.class)
                    .name(domainName)
                    .method(ElementMatchers.named("getIdentifier"))
                    .intercept(FixedValue.value(1))                 
                    .make()
                    .load(Sample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                    .getLoaded();

            Domain clazz = (Domain) domain.newInstance();
            clazz.setIdentifier(1);
            System.out.println(clazz.getIdentifier());
        }
    }   
}

现在我正在使用 Roaster 生成以下内容 class:

public class Adres implements Domain {

    @Parsed(index = 0)
    private int id;
    @Parsed(index = 1)
    private String plaatsNaam;
    @Parsed(index = 2)
    private String straatNaam;
    @Parsed(index = 3)
    private String huisnummer;
    @Parsed(index = 4)
    private String postcode;

    @Override
    public int getIdentifier() {
        return id;
    }
}

但是来自外部源的输入的不断变化的性质使得运行时代码生成比源代码生成更受欢迎。所以理想情况下,我想在运行时创建上面的代码并在之后使用该代码(显然)。

http://bytebuddy.net/#/tutorial 的字段和方法部分确实提到了 ElementMatchers 和拦截器的使用,但坦率地说,我不知道如何让它们工作。

问题:如何使用 Byte Buddy 创建一个实现接口的 POJO,然后在生成的代码上调用方法?

您从未实施 setIdentifier 方法。因此,该方法仍然是抽象的,并且 AbstractMethodError 被抛出。虽然这被 javac 禁止,但 VM 允许您加载这样的类型,因此 Byte Buddy 也允许它。就您而言,这当然不是期望的结果。您可能希望使用 FieldAccessor 实现执行如下操作:

Domain domain = new ByteBuddy()
  .subclass(Domain.class)
  .method(isDeclaredBy(Domain.class))
  .intercept(FieldAccessor.ofField("id").defineAs(int.class, Visibility.PRIVATE))
  .make()
  .load(Sample.class.getClassLoader())
  .getLoaded()
  .newInstance();

  domain.setIdentifier(42);
  System.out.println(domain.getIdentifier());

在上面的 Adres 示例中,setter 实现也缺失;我假设您忘记了它,但如果不是这种情况,请要求澄清。

Edit:您更新的示例仍然建议使用这种方法,但您需要手动定义 setters 和 getter。我计划在某个时候为此添加一个方便的 API,但您应该能够在属性列表的循环中执行此操作。您可以通过在生成的 class 上使用反射 API 或使用 Java.

的反编译器查看生成的 class 文件的反汇编来验证