JavaAgent 简单例子

本文详细介绍了JavaAgent的概念及其在JVM启动过程中的作用。通过实例演示了如何创建和配置JavaAgent,并解释了其运行机制,包括如何通过premain方法在main方法之前进行拦截。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaAgent 是JDK 1.5 以后引入的,也可以叫做Java代理。

JavaAgent 是运行在 main方法之前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法然后再执行 main 方法。

那么如何实现一个 JavaAgent 呢?很简单,只需要增加 premain 方法即可。

看下面的代码和代码中的注释说明:

package com.shanhy.demo.agent;

import java.lang.instrument.Instrumentation;

/**
 * 我的Java代理
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.youkuaiyun.com/catoop/
 * @create    2016年3月30日
 */
public class MyAgent {

    /**
     * 该方法在main方法之前运行,与main方法运行在同一个JVM中
     * 并被同一个System ClassLoader装载
     * 被统一的安全策略(security policy)和上下文(context)管理
     *
     * @param agentOps
     * @param inst
     * @author SHANHY
     * @create  2016年3月30日
     */
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("=========premain方法执行========");
        System.out.println(agentOps);
    }

    /**
     * 如果不存在 premain(String agentOps, Instrumentation inst) 
     * 则会执行 premain(String agentOps)
     *
     * @param agentOps
     * @author SHANHY
     * @create  2016年3月30日
     */
    public static void premain(String agentOps) {
        System.out.println("=========premain方法执行2========");
        System.out.println(agentOps);
    }
}

写完这个类后,我们还需要做一步配置工作。

在 src 目录下添加 META-INF/MANIFEST.MF 文件,内容按如下定义:

Manifest-Version: 1.0
Premain-Class: com.shanhy.demo.agent.MyAgent
Can-Redefine-Classes: true

要特别注意,一共是四行,第四行是空行,还有就是冒号后面的一个空格,如下截图:
这里写图片描述

然后我们打包代码为 myagent.jar

注意打包的时候选择我们自己定义的 MANIFEST.MF
这里写图片描述


接着我们在创建一个带有main方法的主程序工程,截图如下:
这里写图片描述

然后将该主程序打包为 myapp.jar


如何执行 myagent.jar ?我们通过 -javaagent 参数来指定我们的Java代理包,值得一说的是 -javaagent 这个参数的个数是不限的,如果指定了多个,则会按指定的先后执行,执行完各个 agent 后,才会执行主程序的 main 方法。

命令如下:

java -javaagent:G:\myagent.jar=Hello1 -javaagent:G:\myagent.jar=Hello2 -javaagent:G:\myagent.jar=Hello3 -jar myapp.jar

输出结果:

G:\>java -javaagent:G:\myagent.jar=Hello1 -javaagent:G:\myagent.jar=Hello2 -javaagent:G:\myagent.jar=Hello3 -jar myapp.jar
=========premain方法执行========
Hello1
=========premain方法执行========
Hello2
=========premain方法执行========
Hello3
=========main方法执行========

特别提醒:如果你把 -javaagent 放在 -jar 后面,则不会生效。也就是说,放在主程序后面的 agent 是无效的。

比如执行:

java -javaagent:G:\myagent.jar=Hello1 -javaagent:G:\myagent.jar=Hello2 -jar myapp.jar -javaagent:G:\myagent.jar=Hello3

只会有前个生效,第三个是无效的。
输出结果:

G:\>java -javaagent:G:\myagent.jar=Hello1 -javaagent:G:\myagent.jar=Hello2 -jar myapp.jar -javaagent:G:\myagent.jar=Hello3
=========premain方法执行========
Hello1
=========premain方法执行========
Hello2
=========main方法执行========

命令中的Hello1为我们传递给 premain 方法的字符串参数。

至此,我们会使用 javaagent 了,但是单单看这样运行的效果,好像没有什么实际意义嘛。

我们可以用 javaagent 做什么呢?下篇文章我们来介绍如何在项目中应用 javaagent。


最后说一下,还有一种,在main方法执行后再执行代理的方法,因为不常用,而且主程序需要配置 Agent-Class,所以不常用,如果需要自行了解下 agentmain(String agentArgs, Instrumentation inst) 方法。

<think>好的,我现在需要回答用户关于Java Agent的例子的问题。首先,我得回忆一下Java Agent的基本概念,确保自己理解正确。Java Agent是一种能够在JVM启动时或运行时加载的特殊工具,主要用于监控、分析或修改运行中的Java程序。用户需要一个具体的例子,所以我得想一个典型的应用场景,比如性能监控、代码热替换或者AOP增强。 接下来,我应该选择一个简单但能说明问题的例子。比如,使用Java Agent来统计方法的执行时间。这样用户容易理解,而且能展示Agent的主要功能。需要确定如何编写Agent类,使用Instrumentation API,以及如何打包和加载Agent。 然后,考虑步骤分解。首先创建Premain类,实现premain方法,然后使用ClassFileTransformer来转换字节码。这里可能需要用ASM或者Javassist库来操作字节码,但为了简化,可能用简单例子,比如在方法前后插入计时代码。不过,用户可能希望看到完整的代码示例,所以需要确保代码的正确性。 然后,编写示例代码,包括Agent部分和测试程序。要注意包结构和MANIFEST.MF文件的配置,这是容易出错的地方。需要说明如何打包成JAR,以及如何通过命令行参数加载Agent。 另外,用户可能需要了解两种加载方式:启动时加载和运行时动态加载。例子可能只展示启动时加载,但可以提到另一种方式。同时,要注意Java版本兼容性,比如是否支持JDK 8或更高版本。 可能遇到的疑问点包括:如何注册Transformer,如何避免无限循环(当Agent修改自身的方法时),以及如何确保字节码操作的准确性。需要检查示例是否处理了这些问题,比如在Transformer中过滤掉非目标类。 最后,确保回答结构清晰,分步骤说明,代码格式化正确,使用中文,并符合LaTeX要求。可能需要验证示例代码是否实际运行过,避免提供错误的信息。同时,提醒用户实际应用中需要考虑的注意事项,比如性能影响和兼容性问题。</think>Java Agent(Java 代理)是一种在 JVM 层面拦截和修改类字节码的技术,常用于性能监控、代码热替换、AOP 实现等场景。以下通过一个统计方法执行时间的示例说明其用法: --- ### 第一步:创建 Agent 类 ```java // TimeAgent.java import java.lang.instrument.Instrumentation; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; public class TimeAgent { public static void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (className.equals("com/example/TestClass")) { // 使用 ASM/Javassist 修改字节码(此处简化逻辑) System.out.println("拦截类: " + className); } return classfileBuffer; // 返回修改后的字节码 } }); } } ``` --- ### 第二步:配置 MANIFEST.MF 在 `resources/META-INF/MANIFEST.MF` 中添加: ``` Premain-Class: TimeAgent Can-Redefine-Classes: true Can-Retransform-Classes: true ``` --- ### 第三步:打包 Agent ```bash javac TimeAgent.java jar cvmf META-INF/MANIFEST.MF time-agent.jar TimeAgent.class ``` --- ### 第四步:使用 Agent 通过 JVM 参数加载: ```bash java -javaagent:time-agent.jar -jar your-app.jar ``` --- ### 实际效果 当 `com.example.TestClass` 被加载时,Agent 会触发字节码修改。实际开发中通常会配合字节码操作库(如 ASM)插入统计耗时的逻辑: ```java // 伪代码:在方法前后插入计时 long start = System.nanoTime(); try { originalMethod(); // 原方法逻辑 } finally { long cost = System.nanoTime() - start; System.out.println("方法耗时: " + cost + "ns"); } ``` --- ### 关键特性 1. **两种加载方式** - 启动时加载:通过 `-javaagent` 参数 - 运行时挂载:通过 `VirtualMachine.attach()` API 2. **典型应用场景** - APM 工具(如 SkyWalking) - IDE 热部署(如 IntelliJ IDEA) - 代码覆盖率统计(如 JaCoCo) --- $\textcolor{red}{注意}$:实际开发中建议使用成熟的字节码操作库(如 ASM、Byte Buddy),而非直接操作字节码数组。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catoop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值