我们如何在编译时访问 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()
此外,是否可以使用动态生成的 class:
NewEntity newEntity = someCreateMethod(EntityExample.class);
Integer num = newEntity.getNumber();
String sVal = newEntity.getStringVal();
?
编辑:感谢您的帮助,这个例子是我第一次尝试使用 ByteBuddy 库。我想 defineMethod
实际上定义了一个接口方法的实现,而不是只是在 class 中添加了一个随机方法。所以我决定在这里解释一下我到底想完成什么。
对于 class E 中的每个 Date
属性,我想再添加两个字段(以及它们各自的 getter 和 setter),比方说 (atribute name)InitialDate
和 (atribute name)FinalDate
, 这样我就可以为 E
.
中的每个日期使用间隔函数
我想知道是否可以使用代码生成来添加这些方法,而不必为每个 E
.
创建子classes
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 是包含您尝试调用的方法的超级class/ 或接口——您将无法解析 E 上不存在的子类型方法。
这不是 ByteBuddy 的问题,这是您的 class 设计的问题 -- 您应该将要生成的功能设计并分组为可抽象的部分,以便可以通过以下类型公开在编译时有意义。
例如,我们可以使用超类型 '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 个简单的原子行为,对于它们的抽象是显而易见的。
您可以使用反射在任意 dynamically-defined class 上找到任意方法,但这从编码或设计 POV 来看并没有真正意义(您的代码如何知道要调用哪些方法?如果它确实知道,为什么不使用类型来表达它呢?)也不是很高效。
对问题的后续编辑 -- Java Bean 属性通过反射工作,因此查找 "related properties" 的示例(例如第一个/最后一个日期)从已知的属性来看并非没有道理。
然而,可以考虑使用 DateInterval( FirstDate, LastDate) class 这样每个基础只需要 一个 补充 属性 属性。
正如 Thomas 指出的那样,Byte Buddy 在运行时生成 类,因此您的编译器无法在编译期间验证它们的存在。
您可以做的是在构建时应用您的代码生成。如果您的 EntityExample.class
存在于特定模块中,您可以使用 Byte Buddy Maven 或 Gradle 插件增强此模块,然后,在增强之后,让您的编译器验证它们的存在。
您还可以定义接口,例如
interface StringVal {
String getStringVal();
}
你可以要求 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
属性:
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()
此外,是否可以使用动态生成的 class:
NewEntity newEntity = someCreateMethod(EntityExample.class);
Integer num = newEntity.getNumber();
String sVal = newEntity.getStringVal();
?
编辑:感谢您的帮助,这个例子是我第一次尝试使用 ByteBuddy 库。我想 defineMethod
实际上定义了一个接口方法的实现,而不是只是在 class 中添加了一个随机方法。所以我决定在这里解释一下我到底想完成什么。
对于 class E 中的每个 Date
属性,我想再添加两个字段(以及它们各自的 getter 和 setter),比方说 (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 是包含您尝试调用的方法的超级class/ 或接口——您将无法解析 E 上不存在的子类型方法。
这不是 ByteBuddy 的问题,这是您的 class 设计的问题 -- 您应该将要生成的功能设计并分组为可抽象的部分,以便可以通过以下类型公开在编译时有意义。
例如,我们可以使用超类型 '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 个简单的原子行为,对于它们的抽象是显而易见的。
您可以使用反射在任意 dynamically-defined class 上找到任意方法,但这从编码或设计 POV 来看并没有真正意义(您的代码如何知道要调用哪些方法?如果它确实知道,为什么不使用类型来表达它呢?)也不是很高效。
对问题的后续编辑 -- Java Bean 属性通过反射工作,因此查找 "related properties" 的示例(例如第一个/最后一个日期)从已知的属性来看并非没有道理。
然而,可以考虑使用 DateInterval( FirstDate, LastDate) class 这样每个基础只需要 一个 补充 属性 属性。
正如 Thomas 指出的那样,Byte Buddy 在运行时生成 类,因此您的编译器无法在编译期间验证它们的存在。
您可以做的是在构建时应用您的代码生成。如果您的 EntityExample.class
存在于特定模块中,您可以使用 Byte Buddy Maven 或 Gradle 插件增强此模块,然后,在增强之后,让您的编译器验证它们的存在。
您还可以定义接口,例如
interface StringVal {
String getStringVal();
}
你可以要求 Byte Buddy 在你的子类中实现它,如果你将你的子类表示为这个接口,它允许你的编译器验证方法的存在。
除此之外,您的编译器正在做它应该做的事情:告诉您您正在调用一个不存在的方法(当时)。