Byte Buddy:java.lang.AbstractMethodError:调用方法

时间:2016-09-27 19:58:31

标签: java interface code-generation pojo byte-buddy

修改

原始问题中的实际界面和示例域存在缺陷。这是正确的例子。

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类? (我怎么检查这个?)

PRE-EDIT

我试图在运行时使用Byte Buddy API生成一些实现域接口的类(http://bytebuddy.net/#/

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

在成功生成代码后,我想在运行时生成的类中使用这些代码来映射文件(例如解析行并将它们映射到生成的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生成以下类:

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和Interceptors的使用,但坦率地说,我对如何使它们起作用感到茫然。

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

1 个答案:

答案 0 :(得分:1)

您永远不会实施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实现;我假设你忘记了它,但如果不是这样,请求澄清。

修改:您的更新示例仍然建议使用此方法,但您需要手动定义setter和getter。我计划在某些时候为此添加一个便利API,但是您应该能够在循环中为属性列表执行此操作。您可以使用生成的类上的反射API进行验证,或者使用针对Java的反编译器查看生成的类文件的反汇编。