繁体   English   中英

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

[英]How can we access methods generated by ByteBuddy in compilation time?

我写了这个例子:

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属性:

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和setters),比如说(atribute name)InitialDate(atribute name)FinalDate ,以便我可以对中的每个日期使用间隔函数E

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

PS: E不能更改,它属于旧模块。

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

NewA a = eb.create(A.class);
a.getDeadLine(); //inherited
a.getDeadLineFirstDay(); //added 
a.getDeadLineLastDay(); //added

NewA b = eb.create(B.class);
b.getBirthday(); //inherited
b.getBirthdayFirstDay(); //added
b.getBirthdayLastDay(); //added

b.getAnniversary(); //inherited
b.getAnniversaryFirstDay(); //added
b.getAnniversaryLastDay(); //added

PS3:我尝试使用ByteBuddy甚至完成什么? 还有另一种方法吗?

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

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

这不是ByteBuddy的问题,这不是类设计的问题-您应该将要生成的功能设计和分组为可抽象的部分,以便可以通过在编译时有意义的类型来公开它。

例如,我们可以使用超类型'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)类,以便每个基本属性仅需要一个补充属性。

正如Thomas所指出的,Byte Buddy在运行时生成类,以使您的编译器无法在编译期间验证它们的存在。

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

您还可以做的是定义接口,例如

interface StringVal {
  String getStringVal();
}

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

除此之外,您的编译器完全按照预期的方式运行:告诉您正在调用的方法(当时不存在)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM