Java Agent 是一種在目標應用啟動前運行的特殊程序,能通過 Instrumentation API 修改類字節碼,實現 AOP、監控、熱部署等核心功能。傳統字節碼操作(如 ASM)門檻極高,Byte Buddy 以類型安全的 DSL 封裝底層細節,讓普通 Java 開發者也能輕鬆掌握運行時代碼生成。
public class MyAgent {
public static void premain(String args, Instrumentation inst) {
System.setProperty("my-property", "foo"); // 修改環境屬性
// 註冊 ClassFileTransformer 修改字節碼
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className,
Class> classBeingRedefined,
ProtectionDomain pd, byte[] classFileBuffer) {
// 返回修改後的字節碼
return modifiedBytes;
}
});
}
}
Agent-Classjava -javaagent:myAgent.jar=options -jar app.jar| 痛點 | Byte Buddy 方案 |
|---|---|
| 需深度理解 JVM 字節碼指令集 | 類型安全 DSL,以 Java 語法表達字節碼邏輯 |
| 手動解析類層級結構(父類、接口) | 自動分析類繼承體系,智能匹配方法 |
| 反射 API 在類加載前不可用 | 提供 @Origin、@Super 等注解注入上下文信息 |
| 代碼維護成本高,團隊懼怕修改 | 代碼可讀性強,降低認知負擔 |
Class> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World!"))
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
class ToStringInterceptor {
static String intercept(@Origin Method method) {
return "Hello from " + method.getName() + "!";
}
}
// 使用委托
.intercept(MethodDelegation.to(ToStringInterceptor.class))
支持參數注解:@Origin(方法元信息)、@Super(調用原方法)、@AllArguments(所有參數)、自定義注解。
@interface Secured { String user(); }
class Service {
@Secured(user = "ADMIN")
void doSensitiveAction() { /* 敏感操作 */ }
}
class SecurityInterceptor {
static String currentUser = "ANONYMOUS";
static void check(@Origin Method method) {
if (!method.getAnnotation(Secured.class).user().equals(currentUser))
throw new IllegalStateException("Unauthorized");
}
}
// 子類模式(需工廠創建實例)
new ByteBuddy()
.subclass(Service.class)
.method(ElementMatchers.isAnnotatedWith(Secured.class))
.intercept(MethodDelegation.to(SecurityInterceptor.class).andThen(SuperMethodCall.INSTANCE))
.make().load(...).getLoaded().newInstance().doSensitiveAction();
class SecurityAgent {
public static void premain(String args, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.any())
.transform((builder, type) -> builder
.method(ElementMatchers.isAnnotatedWith(Secured.class))
.intercept(MethodDelegation.to(SecurityInterceptor.class)
.andThen(SuperMethodCall.INSTANCE)))
.installOn(inst);
}
}
優勢:直接修改原類字節碼,new Service().doSensitiveAction() 即可生效;支持單元測試無感運行;多 Agent 疊加無耦合。
VirtualMachine.attach() 動態加載 AgentByte Buddy 將字節碼操作從「黑魔法」變為「日常工具」。掌握它,你將擁有修改 JVM 運行行為的超能力,構建出更靈活、可觀測、可演進的 Java 系統。