Byte-buddy:如何重新定义类以添加额外的方法

问题描述 投票:0回答:1

如果我有以下

Class
名为
Dog

public class Dog {
  
  private String name = "Spike";
  private int age = 3;
  
  public Dog() {}
  
  public String getName() {
    return name;
  }
}

我如何声明 ByteBuddy 在

javaagent
中使用以重新定义
Dog
Class
以提供返回
getAge
的方法
int

GOAL:所需的(有效的)

Class
应该如下所示,并且可以通过执行
dog.getAge()

来调用
public class Dog {
  
  private String name = "Spike";
  private int age = 3;
  
  public Dog() {}
  
  public String getName() {
    return name;
  }

  public int getAge() {
    return age;      
  }
}

尝试 1:我尝试将语法更改为我理解的正确语法(下面找到 PitBullInterceptor.class)

new AgentBuilder.Default()
.redefine(Dog.class)
.defineMethod("getAge", int.class, Method.PUBLIC)
.intercept(MethodDelegation.to(PitBullInterceptor.class))
.installOn(instrumentation);

我在尝试中遇到以下问题

The method redefine(Class<Dog>) is undefined for the type AgentBuilder.Default

这基本上是一个语法错误。我不确定使用哪个

DynamicTypes
MethodDelegations
/
ElementMaters
来配置
Agentbuilder
来执行这种类型的重新定义/变基,正如 Rafael Winterhalter 所描述的那样

尝试 2:我尝试将它与 ByteBuddyAgent.install 一起使用

  public static void main(String[] args) throws Exception {
    
    ByteBuddyAgent.install();
    
    new ByteBuddy()
    .redefine(Dog.class)               
    .defineMethod("getValue", int.class, Method.PUBLIC)
    .intercept(MethodDelegation.to(PitBullInterceptor.class))
    .make()
    .load(Dog.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
    .getLoaded()
    .newInstance();
  }
  
  public static class PitBullInterceptor {
    
    private static int age = 1;
    
    public static int getValue() {
      return age;
    }
  }

产生错误:

java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method

尝试 3:最后,我尝试使用内置代理这样做

  public static void premain(String arguments, Instrumentation instrumentation) {
  
    new AgentBuilder.Default()
    .disableClassFormatChanges().with(RetransformationStrategy.RETRANSFORM)
    .with(new ByteBuddy()
    .redefine(PitBull.class)
    .method(ElementMatchers.named("getValue"))
    .intercept(MethodDelegation.to(PitBullInterceptor.class))
    .make()
    .load(Dog.class.getClassLoader()))
    .installOn(instrumentation);
  }
  
  public static class PitBull {
    
    private String name = "Roxy";
    
    public String getName() {
      return name;
    }
  }
  
  public static class PitBullInterceptor {
    
    private static String value = "WoofWoofWoof";
    
    public static String getValue() {
      return value;
    }
  }

出品:

java -javaagent:agent.jar -jar app.jar
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:503)
Caused by: java.lang.IllegalStateException: Class already loaded: class io.xxx.agent.boss.Agent$PitBull
        at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.load(ByteArrayClassLoader.java:363)
        at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$WrappingDispatcher.load(ClassLoadingStrategy.java:367)
        at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:148)
        at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
        at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6317)
        at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6305)
        at io.xxx.agent.boss.Agent.premain(Agent.java:21)
        ... 6 more
*** java.lang.instrument ASSERTION FAILED ***: "result" with message agent load/premain call failed at t:\workspace\open\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 422
FATAL ERROR in native method: processing of -javaagent failed, processJavaStart failed
java interceptor byte-buddy javaagents
1个回答
1
投票

由于JVM 的限制,您不能添加、删除或修改现有类的成员。你必须要么

  1. 在加载类之前添加方法,或者
  2. 在 JVM 启动之前修改磁盘上的类

或者,您可以使用支持这种修改的第三方 JVM(参见:JetBrains runtime with the Enhanced class redefinition flag),但假设这需要普遍运行,它会有点负担。

© www.soinside.com 2019 - 2024. All rights reserved.