我们如何在编译时访问ByteBuddy生成的方法?

时间:2017-01-16 22:17:13

标签: java code-generation byte-buddy

我写了这个例子:

E someCreateMethod(Class<E> clazz) {
    Class<? extends E> dynamicType = new ByteBuddy()
            .subclass(clazz)
            .name("NewEntity")
            .method(named("getNumber"))
            .intercept(FixedValue.value(100))
            .defineField("stringVal", String.class, Visibility.PRIVATE)
            .defineMethod("getStringVal", String.class, Visibility.PUBLIC)
            .intercept(FieldAccessor.ofBeanProperty())
            .make()
            .load(clazz.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

    return dynamicType.newInstance();
}

我想用它来获得重新定义的number atributte:

Integer num = someCreateMethod(EntityExample.class).getNumber();  //(1)

或者获取新定义的stringVal属性:

String sVal = someCreateMethod(EntityExample.class).getStringVal(); //(2)  

我的问题是(1)工作得很好,而(2)没有。我收到以下错误:

Error:(40, 67) java: cannot find symbol

symbol:   method getStringVal()

此外,是否可以使用动态生成的类执行类似的操作:

NewEntity newEntity = someCreateMethod(EntityExample.class);
Integer num = newEntity.getNumber();
String sVal = newEntity.getStringVal();

编辑:感谢您的帮助,这个例子是我第一次尝试使用ByteBuddy库。我认为defineMethod实际上定义了一个接口方法的实现,而不仅仅是在类中添加一个随机方法。所以我决定在这里解释一下我到底想要完成什么。

对于E类中的每个Date属性,我想再添加两个字段(以及它们各自的getter和setter),让我们说(atribute name)InitialDate(atribute name)FinalDate,这样我就可以对E中的每个日期使用间隔功能。

我想知道是否可以使用代码生成来添加这些方法,而无需为每个E创建子类。

PS:E无法更改,它属于传统模块。

PS2:我不知道每个实体E中会有多少个日期属性,但新的属性和方法将使用约定创建(例如__FisrtDay,{{1} }),如下所示:

__LastDay
PS3:我正在尝试用ByteBuddy或者根本不可能实现的目标吗?还有另外一种方法吗?

PS4:我的编辑应该是一个新问题吗?

2 个答案:

答案 0 :(得分:4)

您需要E成为超类/或接口,其中包含您尝试调用的方法 - 您将无法解析E上不存在的子类型方法。

这不是ByteBuddy的问题,这是你的课程设计的一个问题 - 你应该设计&amp;将您想要生成的功能分组为可抽象的部分,因此可以通过在编译时有意义的类型进行公开。

例如,我们可以使用超类型'ValueProvider',然后使用ByteBuddy来定义IntConstantProvider。

public interface ValueProvider<T> {
    public T getValue();
}

Class<? extends ValueProvider<Integer>> dynamicType = new ByteBuddy()
    .subclass(clazz)
    .name("ConstantIntProvider")
    .method(named("getValue"))
    .intercept(FixedValue.value(100))
    // etc.

你的原型有3个独立的功能(如果我们认为非引用私有字段是某些预期行为的存根),没有明显的抽象来包含它们。这可以更好地设计为3个简单的原子行为,抽象将是显而易见的。

您可以使用反射在任意动态定义的类上查找任意方法,但这对于编码或设计POV来说并不是很有意义(您的代码如何知道要调用哪些方法?如果它知道,为什么不使用表达的类型?)也不是非常高效。

编辑后的问题 - Java Bean属性通过反射工作,因此从已知属性中查找“相关属性”(例如“上次/上次日期”)的示例并非不合理。

但是可以考虑使用DateInterval(FirstDate,LastDate)类,以便每个基本属性只需要一个补充属性。

答案 1 :(得分:0)

正如Thomas指出的那样,Byte Buddy在运行时生成类,这样编译器就无法在编译期间验证它们的存在。

您可以做的是在构建时应用代码生成。如果您的EntityExample.class存在于特定模块中,则可以使用Byte Buddy Maven或Gradle插件增强此模块,然后在增强后,允许编译器验证它们的存在。

您还可以做的是定义

等接口
interface StringVal {
  String getStringVal();
}

您可以要求Byte Buddy在您的子类中实现,如果您将子类表示为此接口,则允许编译器验证方法的存在。

除此之外,您的编译器正在完成它应该做的事情:告诉您正在调用一个不存在的方法(当时)。