如何用Byte Buddy/ASM插装JDK8代码采集变量分配及方法参数数据
选择Byte Buddy实现JDK8下的Java代码插装需求
优先选Byte Buddy来实现你的需求,ASM虽然是字节码操作的基础库,但需要手动处理大量底层字节码指令,开发成本高且容易出错;Byte Buddy提供了高阶封装的API,能快速实现方法参数采集和对象分配追踪,同时完美适配JDK8的参数名获取逻辑。
Byte Buddy的核心优势
- 简化字节码操作:无需手动编写字节码指令,通过注解式的
Advice就能定义插装逻辑,大幅降低开发复杂度。 - 原生支持参数名获取:JDK8编译时添加
-parameters参数后,Byte Buddy可以直接通过MethodDescription获取方法的原始参数名,无需额外解析字节码元数据。 - 集成对象分配追踪:结合
AgentBuilder和构造方法插装,能轻松采集对象分配的类型和实例信息。
示例实现代码
1. 插装代理(Agent)代码
import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatchers; import java.lang.instrument.Instrumentation; import java.util.stream.Collectors; public class AllocationAndParameterAgent { public static void premain(String args, Instrumentation inst) { new AgentBuilder.Default() // 匹配需要插装的目标类(可根据业务调整范围) .type(ElementMatchers.nameStartsWith("com.yourpackage")) // 插装所有非构造方法,采集参数信息 .transform((builder, typeDesc, classLoader, module) -> builder .visit(Advice.to(MethodParameterAdvice.class) .on(ElementMatchers.not(ElementMatchers.isConstructor())))) // 插装构造方法,追踪对象分配 .transform((builder, typeDesc, classLoader, module) -> builder .visit(Advice.to(AllocationAdvice.class) .on(ElementMatchers.isConstructor()))) .installOn(inst); } // 方法参数采集逻辑 public static class MethodParameterAdvice { @Advice.OnMethodEnter public static void captureParams( @Advice.Origin MethodDescription method, @Advice.AllArguments Object[] args) { // 获取参数名列表 String paramNames = method.getParameters() .stream() .map(param -> param.getInternalName()) .collect(Collectors.joining(", ")); // 获取参数值列表 String paramValues = args == null ? "无参数" : java.util.Arrays.stream(args) .map(obj -> obj == null ? "null" : obj.toString()) .collect(Collectors.joining(", ")); // 输出格式化日志 System.out.printf("[方法调用] %s.%s | 参数名: [%s] | 参数值: [%s]%n", method.getDeclaringType().getSimpleName(), method.getName(), paramNames, paramValues); } } // 对象分配采集逻辑 public static class AllocationAdvice { @Advice.OnMethodExit public static void captureAllocation(@Advice.This Object instance) { System.out.printf("[对象分配] 类型: %s | 实例哈希: %d%n", instance.getClass().getSimpleName(), instance.hashCode()); } } }
2. 测试代码
public class TestApp { public static void main(String[] args) { User user = createUser("Alice", 25); printUserInfo(user); } public static User createUser(String name, int age) { return new User(name, age); } public static void printUserInfo(User user) { System.out.println("User Info: " + user.getName() + ", " + user.getAge()); } } class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
期望输出格式
[方法调用] TestApp.main | 参数名: [args] | 参数值: [[Ljava.lang.String;@xxxxxx] [方法调用] TestApp.createUser | 参数名: [name, age] | 参数值: [Alice, 25] [对象分配] 类型: User | 实例哈希: xxxxx [方法调用] TestApp.printUserInfo | 参数名: [user] | 参数值: [User@xxxxxx] User Info: Alice, 25
关键注意事项
- 编译Java代码时必须添加
-parameters参数,确保参数名被保留到class文件中,否则Byte Buddy无法获取原始参数名。 - 引入Byte Buddy依赖(Maven示例):
<dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.11</version> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-agent</artifactId> <version>1.14.11</version> </dependency>
- 运行测试程序时,通过
-javaagent指定代理Jar:
java -javaagent:allocation-agent.jar TestApp
内容的提问来源于stack exchange,提问作者farzad
相关产品推荐
相关产品推荐

