亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

Java Agent 入門教程

標簽:
雜七雜八
概述

Java Agent 是一种在运行时插入、替换、增强或者修改Java类和方法的工具。本文为你提供了一个全面的Java Agent入门教程,包括项目结构构建、不同入口的运用(premainagentmain)、MANIFEST配置、使用Maven进行打包以及动态加载的API附件。你将了解如何通过Instrumentation API来实现类文件转换,例如在方法调用时打印信息的功能。通过本文,你将掌握Java Agent的核心构建步骤与关键概念,为实际项目应用打下基础。

Java Agent 项目结构

为构建 Java Agent,创建以下目录结构:

.├── pom.xml└── src    └── main        ├── java        │   └── me        │       └── lotabout        │           └── Launcher.java        └── resources            └── META-INF                └── MANIFEST.MF

premain 与 agentmain 入口理解

Java Agent 的入口有两种模式:

  • premain:在启动 Jar 包时指定,如 java -javaagent:my-agent.jar -jar app.jar,权限较高。
  • agentmain:运行后的 JVM 可动态加载,如 java -jar app.jar 后加载 my-agent.jar,权限较低。

定义如下入口类:

public class Launcher {
    public static void premain(String agentArgs, Instrumentation inst) {
        // 静态加载入口
    }

    public static void agentmain(String agentArgs, Instrumentation inst) {
        // 动态加载入口
    }
}

参数 agentArgs 用于传递给 Agent 的字符串,如 java -javaagent:my-agent.jar=my-agent-args app.jar

动态加载时,transformer 的应用默认只对“未来加载的类”生效,因此在动态加载后通过 Instrumentation#retransformClasses 方法重新应用 transformer

MANIFEST 配置

MANIFEST.MF 文件中指定入口类及权限:

Premain-Class: me.lotabout.Launcher
Agent-Class: me.lotabout.Launcher
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
pom.xml 打包与运行

使用 Maven 打包:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <groupId>me.lotabout</groupId>
    <artifactId>my-agent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>9.4</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-tree</artifactId>
            <version>9.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
动态加载 Attach API

动态加载时,先获取目标进程的 PID,并使用 VirtualMachine.attach(PID) 方法连接进程:

public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
    String pid = args[0];
    String path = Launcher.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    VirtualMachine vm = VirtualMachine.attach(pid);
    try {
        vm.loadAgent(path);
    } finally {
        vm.detach();
    }
}
Instrumentation API 使用与示例

使用 Instrumentation 接口,如 Instrumentation#addTransformer 添加 ClassFileTransformer

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new MyTransformer(agentArgs), true);
}

ClassFileTransformer 示例 - 打印方法调用

创建 MyTransformer 类:

public class MyTransformer implements ClassFileTransformer {
    private String prefixOfclassToPrint = "";
    public MyTransformer(String prefixOfclassToPrint) {
        this.prefixOfclassToPrint = prefixOfclassToPrint.replace(".", "/");
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if (!className.startsWith(this.prefixOfclassToPrint)) {
            return classfileBuffer;
        }
        System.out.println("transforming class: " + className);
        ClassNode cn = new ClassNode(Opcodes.ASM4);
        ClassReader cr = new ClassReader(classfileBuffer);
        cr.accept(cn, 0);
        for (MethodNode method : cn.methods) {
            System.out.println("patching Method: " + method.name);
            InsnList list = new InsnList();
            list.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
            list.add(new LdcInsnNode(">> calling Method: " + method.name));
            list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
            method.instructions.insert(list);
        }
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);
        return cw.toByteArray();
    }
}
总结

本文介绍了构建 Java Agent 的基础,从项目结构、入口理解、MANIFEST 配置、pom.xml 打包、动态加载、以及使用 Instrumentation API 的示例,包括 ClassFileTransformer 的简单应用,实现了在方法调用时打印信息的功能。通过本文,读者可以逐步掌握构建 Java Agent 的核心步骤和关键概念。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消