📌 概述
在现代 Java 应用开发中,我们常常需要在不修改原始代码的前提下,对类或方法进行增强(Instrumentation),例如:
- 方法执行耗时统计
 - 日志输出增强
 - 接口调用链追踪(如 SkyWalking、Pinpoint)
 - AOP 编程
 - 热修复(HotFix)
 
实现这些功能的关键技术之一就是 Java Agent + Byte Buddy。本文将带你从零开始,一步步掌握如何使用 Java Agent 和 Byte Buddy 在运行时动态修改字节码,并实现一个简单的 方法耗时统计插件。
🔍 什么是 Java Agent?
Java Agent 是 JVM 提供的一种机制,允许你在 JVM 启动前或运行时加载并修改类的字节码。其核心能力是通过 Instrumentation API 对类进行重新定义(redefine)和转换(transform)。
常见用途包括:
- 性能监控(如统计方法耗时)
 - APM 工具(如 SkyWalking、Zipkin)
 - Mock 框架(如 Mockito)
 - 热部署/热修复
 - 日志增强等
 
🛠️ 什么是 Byte Buddy?
Byte Buddy 是一个强大的 Java 字节码操作库,简化了 Java Agent 的开发过程。它提供了友好的 DSL 风格 API,无需直接编写 ASM 或理解 JVM 字节码指令即可完成复杂的方法拦截和增强。
✅ 特性:
- 支持运行时生成类
 - 支持拦截任意方法
 - 可用于 Java Agent 或普通项目
 - Spring Boot、Mockito、Hibernate 等框架底层都在使用
 
🧩 示例目标
我们希望通过 Java Agent + Byte Buddy,在不修改源码的情况下:
- 拦截指定类(如 
com.example.MyService)中的所有方法 - 输出方法名、执行时间(毫秒)、参数值
 - 支持运行时加载 Agent(即 
-javaagent方式) 
🧱 项目结构
byte-buddy-agent-demo/
├── agent/
│   ├── src/main/java/
│   │   └── com/example/agent/MyAgent.java
│   └── pom.xml (构建 agent jar)
├── app/
│   ├── src/main/java/
│   │   └── com/example/app/MyService.java
│   └── pom.xml
└── README.md🧪 步骤一:创建 Java Agent 模块
MyAgent.java
package com.example.agent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class MyAgent {
    public static void premain(String args, Instrumentation inst) {
        new AgentBuilder.Default()
            .type(ElementMatchers.named("com.example.app.MyService")) // 要拦截的类
            .transform((builder, typeDescription, classLoader, module) ->
                builder.method(ElementMatchers.any()) // 拦截所有方法
                    .intercept(Advice.to(MethodMonitor.class)) // 使用 Advice 增强
            ).installOn(inst);
    }
}MethodMonitor.java
package com.example.agent;
import net.bytebuddy.asm.Advice;
public class MethodMonitor {
    @Advice.OnMethodEnter
    public static long enter() {
        return System.currentTimeMillis();
    }
    @Advice.OnMethodExit
    public static void exit(@Advice.Enter long startTime,
                            @Advice.Origin String method,
                            @Advice.AllArguments Object[] args) {
        long duration = System.currentTimeMillis() - startTime;
        System.out.printf("[ByteBuddy] 方法: %s, 参数: %s, 耗时: %d ms%n",
                method, argsToString(args), duration);
    }
    private static String argsToString(Object[] args) {
        if (args == null || args.length == 0) return "";
        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            sb.append(arg).append(", ");
        }
        return sb.length() > 0 ? sb.substring(0, sb.length() - 2) : "";
    }
}📦 步骤二:配置 MANIFEST.MF(构建 agent.jar)
在 pom.xml 中添加如下配置,确保生成的 jar 包包含 Premain-Class 属性:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifestEntries>
                <Premain-Class>com.example.agent.MyAgent</Premain-Class>
                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                <Can-Retransform-Classes>true</Can-Retransform-Classes>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>构建 agent:
cd agent
mvn clean package生成的 agent jar 文件路径:target/agent-1.0.jar
🏃♂️ 步骤三:编写测试应用
MyService.java
package com.example.app;
public class MyService {
    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        service.sayHello("Alice");
        service.processData(42, true);
    }
    public String sayHello(String name) {
        try {
            Thread.sleep(100); // 模拟耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello, " + name;
    }
    public void processData(int id, boolean flag) {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Processed data: id=" + id + ", flag=" + flag);
    }
}▶️ 运行方式:使用 -javaagent 加载 Agent
java -javaagent:/path/to/agent-1.0.jar -cp app/target/classes com.example.app.MyService输出结果示例:
[ByteBuddy] 方法: sayHello, 参数: Alice, 耗时: 102 ms
Hello, Alice
[ByteBuddy] 方法: processData, 参数: 42, true, 耗时: 301 ms
Processed data: id=42, flag=true📊 适用场景
场景  | 描述  | 
方法耗时统计  | 统计接口响应时间,定位慢方法  | 
日志增强  | 添加请求上下文日志  | 
APM 监控  | 如 SkyWalking、Pinpoint 底层原理  | 
热修复  | 替换有问题的方法逻辑  | 
Mock 测试  | Mockito 等框架内部机制  | 
💡 小技巧:结合 IDE 调试 Agent
你可以在 IntelliJ IDEA 中配置 VM options 来调试 Agent:
-javaagent:/path/to/agent-1.0.jar然后设置断点,查看字节码增强前后的方法变化。
✅ 总结
通过本文你已经掌握了:
- Java Agent 的基本原理与作用
 - 如何使用 Byte Buddy 动态增强类方法
 - 如何拦截方法执行并输出耗时信息
 - 构建可发布的 agent jar 包
 - 结合实际业务代码验证效果
 
Java Agent + Byte Buddy 是 JVM 领域非常强大的组合,尤其适用于非侵入式的性能分析、诊断工具开发和系统增强。掌握它将极大提升你在高级 Java 开发中的实战能力。
📢 如果你想进一步学习:
- Byte Buddy 官方文档
 - JVM Tool Interface (JVMTI)
 - SkyWalking Agent 源码
 
📌 欢迎点赞、收藏 & 分享给更多 Java 开发者!
如需我为你打包完整的 Maven 工程 ZIP 文件(含 agent + demo app),或者生成 Markdown 格式的文章内容模板(方便复制到 51CTO 编辑器),请告诉我,我可以一键帮你整理好。是否需要?✅










