如何使用 Byte Buddy 将方法正确添加到现有 class
How to correctly add a method to an existing class with Byte Buddy
我面临与此处描述的问题相同的问题 ,但是我不确定如何根据我的用例调整解决方案:
我试图通过将方法实现委托给
拦截器。 ActiveRecord
基数class定义如下:
public class ActiveRecord {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private static IllegalStateException implementationMissing() {
return new IllegalStateException(
"This method must be overridden in subclasses");
}
public static Long count(){
throw implementationMissing();
}
public void save(){
throw implementationMissing();
}
// extra methods omitted
}
A child class 将按如下方式扩展活动记录:
class MapText extends ActiveRecord{
private String text;
private String description;
private double wgs84Latitude;
private double wgs84Longitude;
// getters and setters omitted
}
使用 Byte Buddy,我尝试将计数和保存方法委托给拦截器 class,如下所示:
@Test
void testRedefine(){
ByteBuddyAgent.install();
new ByteBuddy().redefine(MapText.class)
.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.defineMethod("count", Long.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.make()
.load(MapText.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
MapText mapText = new MapText();
// set properties
mapText.save();
MapText.count();
}
生成以下异常:
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
如果我在 MapText 中为 save()
和 count()
添加空的“占位符”方法,那么一切正常。
我应该如何调整我的代码以使委托工作而不需要子class中的空占位符方法?
编辑:根据反馈更改代码以使用 AgentBuilder API
@Test
void testRedefine(){
ByteBuddyAgent.install();
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.type(ElementMatchers.named("pkg.MapText"))
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
return builder.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class));
}
}).with(new ListenerImpl()).installOnByteBuddyAgent();
CallTextSave callTextSave = new CallTextSave();
callTextSave.save();
}
CallTextSave
封装了MapText
class并调用保存方法。不幸的是MapText.save()
没有被拦截。
public class CallTextSave {
public void save(){
MapText text = new MapText();
text.save(); // Method not intercepted
}
}
如果您想以这种方式更改代码,则需要在首次加载之前执行此操作。您可以通过使用 AgentBuilder API 定义 Java 代理来实现。您必须避免在代理代码中引用加载的 class,而应使用 named 作为将字符串名称作为参数的匹配器。
或者,您可以通过使用 TypePool.Default 解析 class 来重新定义 main 方法中的 class。同样,按名称解析 TypeDescription 并避免加载它。此外,将实际代码移动到不同的 class,因为 JVM 验证器将加载有问题的 class。
后一种方法只有在您控制应用程序的生命周期时才有可能。
我面临与此处描述的问题相同的问题
我试图通过将方法实现委托给
拦截器。 ActiveRecord
基数class定义如下:
public class ActiveRecord {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private static IllegalStateException implementationMissing() {
return new IllegalStateException(
"This method must be overridden in subclasses");
}
public static Long count(){
throw implementationMissing();
}
public void save(){
throw implementationMissing();
}
// extra methods omitted
}
A child class 将按如下方式扩展活动记录:
class MapText extends ActiveRecord{
private String text;
private String description;
private double wgs84Latitude;
private double wgs84Longitude;
// getters and setters omitted
}
使用 Byte Buddy,我尝试将计数和保存方法委托给拦截器 class,如下所示:
@Test
void testRedefine(){
ByteBuddyAgent.install();
new ByteBuddy().redefine(MapText.class)
.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.defineMethod("count", Long.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.make()
.load(MapText.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
MapText mapText = new MapText();
// set properties
mapText.save();
MapText.count();
}
生成以下异常:
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
如果我在 MapText 中为 save()
和 count()
添加空的“占位符”方法,那么一切正常。
我应该如何调整我的代码以使委托工作而不需要子class中的空占位符方法?
编辑:根据反馈更改代码以使用 AgentBuilder API
@Test
void testRedefine(){
ByteBuddyAgent.install();
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.type(ElementMatchers.named("pkg.MapText"))
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
return builder.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class));
}
}).with(new ListenerImpl()).installOnByteBuddyAgent();
CallTextSave callTextSave = new CallTextSave();
callTextSave.save();
}
CallTextSave
封装了MapText
class并调用保存方法。不幸的是MapText.save()
没有被拦截。
public class CallTextSave {
public void save(){
MapText text = new MapText();
text.save(); // Method not intercepted
}
}
如果您想以这种方式更改代码,则需要在首次加载之前执行此操作。您可以通过使用 AgentBuilder API 定义 Java 代理来实现。您必须避免在代理代码中引用加载的 class,而应使用 named 作为将字符串名称作为参数的匹配器。
或者,您可以通过使用 TypePool.Default 解析 class 来重新定义 main 方法中的 class。同样,按名称解析 TypeDescription 并避免加载它。此外,将实际代码移动到不同的 class,因为 JVM 验证器将加载有问题的 class。
后一种方法只有在您控制应用程序的生命周期时才有可能。