简体   繁体   English

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

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

I wrote this example: 我写了这个例子:

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();
}

And I would like to use it to get the redefined number atributte: 我想用它来获取重新定义的number属性:

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

Or to get the newly defined stringVal attribute: 或获取新定义的stringVal属性:

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

My problem is that (1) works pretty fine, while (2) doesn't. 我的问题是(1)效果很好,而(2)效果不好。 I get the following error: 我收到以下错误:

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

symbol:   method getStringVal()

Also, is it possible to do something like this with a dynamic generated class: 另外,是否可以对动态生成的类执行以下操作:

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

?

EDIT: I appreciate your help, this example was my first attempt on using ByteBuddy library. 编辑:感谢您的帮助,此示例是我第一次尝试使用ByteBuddy库。 I figured that defineMethod actually defines an implementation of an interface method, not just add a random method to the class. 我发现defineMethod实际上定义了接口方法的实现,而不仅仅是在类中添加随机方法。 So I decided to explain here what exactly I'm trying to accomplish. 所以我决定在这里解释我到底要完成什么。

For every Date attribute in a class E, I want to add two more fields (and theirs respectives getters and setters), let's say (atribute name)InitialDate and (atribute name)FinalDate , so that I can use intervals functinality for every date in E . 对于E类中的每个Date属性,我想再添加两个字段(以及它们各自的getter和setters),比如说(atribute name)InitialDate(atribute name)FinalDate ,以便我可以对中的每个日期使用间隔函数E

I was wondering if I could use code-generation to add those methods without having to create subclasses for every E . 我想知道是否可以使用代码生成来添加这些方法,而不必为每个E创建子类。

PS: E can't be changed, it belongs to a legacy module. PS: E不能更改,它属于旧模块。

PS2: I don't know how many date attributes there would be in each entity E , but the new attibutes and methods would be created using conventions (for example __FisrtDay , __LastDay ), as shown below: 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: Is what I'm trying to accomplish even possible with ByteBuddy or at all? PS3:我尝试使用ByteBuddy甚至完成什么? Is there another way? 还有另一种方法吗?

PS4: Should my EDIT have been a new question? PS4:我的编辑应该是一个新问题吗?

You need E to be a superclass/ or interface which includes the methods you are trying to call -- you will not be able to resolve subtyped methods which do not exist on E. 您需要E成为包含要尝试调用的方法的超类/或接口-您将无法解析E上不存在的子类型方法。

This is not a ByteBuddy issue, this is an issue of your class design -- you should design & group the functionality you intend to generate into abstractable parts, so it can be exposed via types which are meaningful at compile time. 这不是ByteBuddy的问题,这不是类设计的问题-您应该将要生成的功能设计和分组为可抽象的部分,以便可以通过在编译时有意义的类型来公开它。

For example, we could use a supertype 'ValueProvider' and then use ByteBuddy to define an IntConstantProvider. 例如,我们可以使用超类型'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.

Your prototype had 3 separate functionalities (if we consider unreference private fields to be the stub of some intended behavior) with no obvious abstraction to encompass them. 您的原型具有3个单独的功能(如果我们将非引用私有字段视为某些预期行为的残根),则没有明显的抽象来包含它们。 This could be better designed as 3 simple atomic behaviors, for which the abstractions would be obvious. 最好将其设计为3个简单的原子行为,对于这些行为,抽象是显而易见的。

You could use reflection to find arbitrary methods on a arbitrary dynamically-defined class, but this is not really meaningful from a coding or design POV (how does your code know which methods to call? if it does know, why not use a type to express that?) nor is it very performant. 您可以使用反射在任意动态定义的类上查找任意方法,但这对于编码或设计POV而言并没有真正的意义(您的代码如何知道要调用的方法?如果知道,为什么不使用类型来表达出来吗?)也不是很出色。

FOLLOWING EDIT TO QUESTION -- Java Bean properties work by reflection, so the example of finding "related properties" (such as First/ Last Date) from known properties is not unreasonable. 按照问题进行编辑 -Java Bean属性是通过反射起作用的,因此从已知属性中查找“相关属性”(例如“第一/最后日期”)的例子并非不合理。

However it could be considered to use a DateInterval( FirstDate, LastDate) class so that only one supplementary property is needed per- base property. 但是,可以考虑使用DateInterval(FirstDate,LastDate)类,以便每个基本属性仅需要一个补充属性。

As Thomas points out, Byte Buddy generates classes at runtime such that your compiler cannot validate their existance during compile time. 正如Thomas所指出的,Byte Buddy在运行时生成类,以使您的编译器无法在编译期间验证它们的存在。

What you can do is to apply your code generation at build time. 您可以做的是在构建时应用代码生成。 If your EntityExample.class exists in a specific module, you can enhance this module with the Byte Buddy Maven or Gradle plugin and then, after enhancement, allow your compiler to validate their existance. 如果您的EntityExample.class存在于特定模块中,则可以使用Byte Buddy Maven或Gradle插件来增强此模块,然后在增强后允许编译器验证其存在。

What you can also do would be to define interfaces like 您还可以做的是定义接口,例如

interface StringVal {
  String getStringVal();
}

which you can ask Byte Buddy to implement in your subclass which allows your compiler to validate the method's existance if you represent your subclass as this interface. 您可以要求Byte Buddy在您的子类中实现,如果您将子类表示为此接口,则允许编译器验证方法的存在。

Other than that, your compiler is doing exactly what it is supposed to do: telling you that you are calling a method that does not exist (at that time). 除此之外,您的编译器完全按照预期的方式运行:告诉您正在调用的方法(当时不存在)。

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

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