Java Agent 提供 JVM 級攔截能力,可在類加載前動態修改字節碼,實現無侵入 AOP。本文完整演示:為所有 @ToString 註解類自動生成 toString() 方法。
premain、使用 Byte Buddy 轉換字節碼、打包為含 Premain-Class 的 JAR// ToString.java - 運行時保留註解
@Retention(RetentionPolicy.RUNTIME)
public @interface ToString {}
// Foo.java - 目標類
@ToString
public class Foo {}
// Main.java - 測試入口
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
System.out.println(foo.toString()); // 期望輸出 "test" 而非默認 hashcode
}
}
未加 Agent 運行輸出:Foo@15db9742 (Object 默認實現)
public class ToStringAgent {
public static void premain(String args, Instrumentation inst) {
System.out.println("print pre main");
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(ToString.class)) // 匹配帶註解的類
.transform(new AgentBuilder.Transformer() {
@Override
public Builder> transform(Builder> builder, TypeDescription typeDesc, ClassLoader cl) {
return builder
.method(ElementMatchers.named("toString")) // 攔截 toString
.intercept(FixedValue.value("test")); // 固定返回 "test"
}
})
.installOn(inst); // 安裝到 Instrumentation
}
}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>com.chosen0ne.ByteCode.agent.ToStringAgent</Premain-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
jar cvfm agent.jar MANIFEST.MF -C . com lib
.
├── agent.jar # Agent JAR (含 Premain-Class)
├── classes/ # 目標程序編譯輸出
│ └── com/chosen0ne/agent/test/
│ ├── Foo.class
│ ├── Main.class
│ └── ToString.class
└── lib/
└── byte-buddy-1.2.3.jar # Byte Buddy 依賴
# 運行命令 (關鍵:-javaagent 指定 agent.jar)
java -cp classes:lib/byte-buddy-1.2.3.jar -javaagent:agent.jar com.chosen0ne.agent.test.Main
print pre main
test
✅ 成功攔截並修改 Foo.toString() 返回值!
# 錯誤示範:目標程序打成 JAR 會導致 ClassNotFoundException
java -cp classes:lib/byte-buddy-1.2.3.jar -javaagent:agent.jar -jar target-app.jar
# 报错:NoClassDefFoundError: net/bytebuddy/matcher/ElementMatcher
原因:Agent 類加載器隔離導致依賴不可見。解決:目標程序以 -cp classes 形式運行類文件,而非 JAR。
| 維度 | ASM | Byte Buddy |
|---|---|---|
| 抽象層級 | 字節碼指令級 | 高級 DSL (方法攔截、註解匹配) |
| 學習曲線 | 極陡峭 | 平緩、文檔豐富 |
| 適用場景 | 極致性能、底層框架 | 業務級 AOP、動態代理增強 |
Java Agent + Byte Buddy 讓「運行時修改類行為」變得可控、可維護,是實現監控、熱修復、無侵入日志、安全審計等基礎設施的利器。