首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java Agent 實戰入門:基於 Byte Buddy 實現運行時代碼動態修改 (AOP)

     編輯:Java 綜合教學

Java Agent 實戰入門:基於 Byte Buddy 實現運行時代碼動態修改 (AOP)

Java Agent 提供 JVM 級攔截能力,可在類加載前動態修改字節碼,實現無侵入 AOP。本文完整演示:為所有 @ToString 註解類自動生成 toString() 方法。

架構概覽

  • 目標程序:被修改的應用 (含註解、測試類、主類)
  • Agent 程序:實現 premain、使用 Byte Buddy 轉換字節碼、打包為含 Premain-Class 的 JAR

1. 目標程序代碼

// 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 默認實現)

2. Agent 核心代碼 (Byte Buddy)

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
    }
}

3. 打包關鍵:MANIFEST.MF 指定 Premain-Class

Maven 方式 (推薦)

<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 命令

jar cvfm agent.jar MANIFEST.MF -C . com lib

4. 目錄結構與運行命令

.
├── 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

5. 運行結果

print pre main
test

✅ 成功攔截並修改 Foo.toString() 返回值!

重要坑點:目標程序不可打包為 JAR

# 錯誤示範:目標程序打成 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。

Byte Buddy vs ASM 對比

維度ASMByte Buddy
抽象層級字節碼指令級高級 DSL (方法攔截、註解匹配)
學習曲線極陡峭平緩、文檔豐富
適用場景極致性能、底層框架業務級 AOP、動態代理增強

Java Agent + Byte Buddy 讓「運行時修改類行為」變得可控、可維護,是實現監控、熱修復、無侵入日志、安全審計等基礎設施的利器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved