You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过ByteBuddy在JavaAgent中为Dog类新增getAge方法

问题描述

现有如下Dog类:

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

需要在JavaAgent中使用ByteBuddy重定义Dog类,新增一个返回int类型的getAge方法,使得最终类可以通过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

代码:

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

报错:

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

属于语法错误,不清楚该用哪些DynamicTypes、MethodDelegations或ElementMatchers配置AgentBuilder实现重定义/重基操作。

尝试2

结合ByteBuddyAgent.install使用:

public static void main(String[] args) throws Exception {
    
    ByteBuddyAgent.install();
    
    new ByteBuddy()
    .redefine(Dog.class)               
    .defineMethod("getAge", 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 getAge() {
      return age;
    }
  }

报错:

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

尝试3

通过构建的Agent实现:

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

执行命令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
解决方案

错误原因分析

  1. 尝试1:AgentBuilder.Default没有直接的redefine(Class)方法,正确方式是通过type()匹配目标类,再结合transform()进行修改。
  2. 尝试2:JVM类重定义机制不允许在类已加载后新增方法,必须在类加载前通过Agent增强,或者使用ByteBuddy的rebase机制,但新增方法更适合在类加载阶段拦截处理。
  3. 尝试3:代码逻辑混乱,错误对PitBull类进行重定义,AgentBuilder用法完全错误,且PitBull作为Agent内部类已被加载,导致重复加载报错。

正确实现代码

要实现新增getAge方法,需在premain中通过AgentBuilder拦截Dog类的加载过程,使用defineMethod新增方法,并通过FieldAccessor直接返回已有age字段:

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;

public class DogAgent {
    public static void premain(String args, Instrumentation instrumentation) {
        new AgentBuilder.Default()
                // 匹配目标Dog类,替换为实际全限定名
                .type(ElementMatchers.named("com.yourpackage.Dog"))
                // 对匹配到的类进行转换
                .transform((builder, typeDescription, classLoader, module) ->
                        builder.defineMethod("getAge", int.class, java.lang.reflect.Modifier.PUBLIC)
                                // 直接返回age字段的值
                                .intercept(FieldAccessor.ofField("age")))
                // 安装Agent
                .installOn(instrumentation);
    }
}

说明

  • ElementMatchers.named("com.yourpackage.Dog")精准匹配目标类,需替换成实际的Dog类全限定名。
  • defineMethod用于新增public int getAge()方法。
  • FieldAccessor.ofField("age")直接绑定到Dog类的age私有字段,ByteBuddy会自动生成返回该字段值的方法体,无需额外编写拦截器。
  • 这种方式在类加载前进行增强,完全符合JVM类修改规则,不会出现新增方法不被允许的问题。

打包与运行

  1. 确保项目依赖ByteBuddy:
<!-- Maven依赖 -->
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-agent</artifactId>
    <version>1.14.12</version>
</dependency>
  1. 打包Agent时,在MANIFEST.MF中指定Premain-Class: com.yourpackage.DogAgent。
  2. 运行应用:
java -javaagent:dog-agent.jar -jar your-app.jar

内容的提问来源于stack exchange,提问作者stackoverflow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:02:55