IDEA + maven 零基础构建 java agent 项目

简介: Java Agent(java 探针)虽说在 jdk1.5 之后就有了,但是对于绝大多数的业务开发 javaer 来说,这个东西还是比较神奇和陌生的;虽说在实际的业务开发中,很少会涉及到 agent 开发,但是每个 java 开发都用过,比如使用 idea 写了个 HelloWorld.java,并运行一下, 仔细看控制台输出

image.png


Java Agent(java 探针)虽说在 jdk1.5 之后就有了,但是对于绝大多数的业务开发 javaer 来说,这个东西还是比较神奇和陌生的;虽说在实际的业务开发中,很少会涉及到 agent 开发,但是每个 java 开发都用过,比如使用 idea 写了个 HelloWorld.java,并运行一下, 仔细看控制台输出


image.png


本篇将作为 Java Agent 的入门篇,手把手教你开发一个统计方法耗时的 Java Agent


I. Java Agent 开发



首先明确我们的开发环境,选择 IDEA 作为编辑器,maven 进行包管理


1. 核心逻辑


创建一个新的项目(or 子 module),然后我们新建一个 SimpleAgent 类


public class SimpleAgent {
    /**
     * jvm 参数形式启动,运行此方法
     *
     * @param agentArgs
     * @param inst
     */
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("premain");
    }
    /**
     * 动态 attach 方式启动,运行此方法
     *
     * @param agentArgs
     * @param inst
     */
    public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("agentmain");
    }
}
复制代码


我们先忽略上面两个方法的具体玩法,先简单看一下这两个方法的区别,注释上也说了


  • jvm 参数形式: 调用 premain 方法
  • attach 方式: 调用 agentmain 方法


其中 jvm 方式,也就是说要使用这个 agent 的目标应用,在启动的时候,需要指定 jvm 参数 -javaagent:xxx.jar,当我们提供的 agent 属于基础必备服务时,可以用这种方式


当目标应用程序启动之后,并没有添加-javaagent加载我们的 agent,依然希望目标程序使用我们的 agent,这时候就可以使用 attach 方式来使用(后面会介绍具体的使用姿势),自然而然的会想到如果我们的 agent 用来 debug 定位问题,就可以用这种方式


2. 打包


上面一个简单 SimpleAgent 就把我们的 Agent 的核心功能写完了(就是这么简单),接下来需要打一个 Jar 包


通过 maven 插件,可以比较简单的输出一个合规的 java agent 包,有两种常见的使用姿势


a. pom 指定配置


在 pom.xml 文件中,添加如下配置,请注意一下manifestEntries标签内的参数


<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifestEntries>
                        <Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>
                        <Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>
                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                    </manifestEntries>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>attached</goal>
                    </goals>
                    <phase>package</phase>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
复制代码


然后通过 mvn assembly:assembly 命令打包,在target目录下,可以看到一个后缀为jar-with-dependencies的 jar 包,就是我们的目标


b. MANIFEST.MF 配置文件


通过配置文件MANIFEST.MF,可能更加常见,这里也简单介绍下使用姿势


  • 在资源目录(Resources)下,新建目录META-INF
  • META-INF目录下,新建文件MANIFEST.MF


文件内容如下


Manifest-Version: 1.0
Premain-Class: com.git.hui.agent.SimpleAgent
Agent-Class: com.git.hui.agent.SimpleAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
复制代码


请注意,最后的一个空行(如果我上面没有显示的话,多半是 markdown 渲染有问题),不能少,在 idea 中,删除最后一行时,会有错误提醒


image.png


然后我们的pom.xml配置,需要作出对应的修改

<build>
  <plugins>
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-assembly-plugin</artifactId>
          <configuration>
              <descriptorRefs>
                  <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
              <archive>
                  <manifestFile>
                      src/main/resources/META-INF/MANIFEST.MF
                  </manifestFile>
                  <!--<manifestEntries>-->
                      <!--<Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>-->
                      <!--<Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>-->
                      <!--<Can-Redefine-Classes>true</Can-Redefine-Classes>-->
                      <!--<Can-Retransform-Classes>true</Can-Retransform-Classes>-->
                  <!--</manifestEntries>-->
              </archive>
          </configuration>
          <executions>
              <execution>
                  <goals>
                      <goal>attached</goal>
                  </goals>
                  <phase>package</phase>
              </execution>
          </executions>
      </plugin>
  </plugins>
</build>
复制代码


同样通过mvn assembly:assembly命令打包


II. Agent 使用



agent 有了,接下来就是需要测试一下使用 agent 的使用了,上面提出了两种方式,我们下面分别进行说明


1. jvm 参数


首先新建一个 demo 项目,写一个简单的测试类


public class BaseMain {
    public int print(int i) {
        System.out.println("i: " + i);
        return i + 2;
    }
    public void run() {
        int i = 1;
        while (true) {
            i = print(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        BaseMain main = new BaseMain();
        main.run();
        Thread.sleep(1000 * 60 * 60);
    }
}
复制代码


测试类中,有一个死循环,各 1s 调用一下 print 方法,IDEA 测试时,可以直接在配置类,添加 jvm 参数,如下


image.png


请注意上面红框的内容为上一节打包的 agent 绝对地址: -

javaagent:/Users/..../target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar

执行 main 方法之后,会看到控制台输出


image.png


请注意上面的premain, 这个就是我们上面的SimpleAgent中的premain方法输出,且只输出了一次


2. attach 方式


在使用 attach 方式时,可以简单的理解为要将我们的 agent 注入到目标的应用程序中,所以我们需要自己起一个程序来完成这件事情


public class AttachMain {
    public static void main(String[] args)
            throws IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
        // attach方法参数为目标应用程序的进程号
        VirtualMachine vm = VirtualMachine.attach("36633");
        // 请用你自己的agent绝对地址,替换这个
        vm.loadAgent("/Users/......./target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");
    }
}
复制代码


上面的逻辑比较简单,首先通过jps -l获取目标应用的进程号


image.png


当上面的 main 方法执行完毕之后,控制台会输出类似下面的两行日志,可以简单的理解为我连上目标应用,并丢了一个 agent,然后挥一挥衣袖不带走任何云彩的离开了


Connected to the target VM, address: '127.0.0.1:63710', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:63710', transport: 'socket'
复制代码


接下来再看一下上面的 BaseMain 的输出,中间夹着一行agentmain, 就表明 agent 被成功注入进去了

image.png


3. 小结


本文介绍了 maven + idea 环境下,手把手教你开发一个 hello world 版 JavaAgent 并打包的全过程


两个方法

方法 说明 使用姿势
premain() agent 以 jvm 方式加载时调用,即目标应用在启动时,指定了 agent -javaagent:xxx.jar
agentmain() agent 以 attach 方式运行时调用,目标应用程序正常工作时使用 VirtualMachine.attach(pid)来指定目标进程号
vm.loadAgent("...jar")加载 agent


两种打包姿势

打包为可用的 java agent 时,需要注意配置参数,上面提供了两种方式,一个是直接在pom.xml中指定配置

<manifestEntries>
    <Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>
    <Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>
    <Can-Redefine-Classes>true</Can-Redefine-Classes>
    <Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
复制代码


另外一个是在配置文件 META-INF/MANIFEST.MF 中写好(需要注意最后一个空行不可或缺)

Manifest-Version: 1.0
Premain-Class: com.git.hui.agent.SimpleAgent
Agent-Class: com.git.hui.agent.SimpleAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
复制代码


当然本篇内容看完之后,会发现对 java agent 的实际开发还是不太清楚,难道 agent 就是在前面输出一行hello world就完事了么,这和想象中的完全不一样啊


下一篇博文将手把手教你实现一个方法统计耗时的 java agent 包,将详细说明利用接口Instrumentation来实现字节码修改,从而是实现功能增强


II. 其他



0. 源码




相关文章
|
2月前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
79 11
|
2天前
|
Java Maven
Idea配置项目的热启动
Idea配置项目的热启动
28 5
Idea配置项目的热启动
|
16天前
|
存储 NoSQL Java
使用Java和Spring Data构建数据访问层
本文介绍了如何使用 Java 和 Spring Data 构建数据访问层的完整过程。通过创建实体类、存储库接口、服务类和控制器类,实现了对数据库的基本操作。这种方法不仅简化了数据访问层的开发,还提高了代码的可维护性和可读性。通过合理使用 Spring Data 提供的功能,可以大幅提升开发效率。
60 21
|
28天前
|
监控 Java API
【潜意识Java】使用SpringBoot构建高效的RESTfulAPI
本文介绍了使用Spring Boot构建RESTful API的完整流程,涵盖从项目创建到API测试的各个步骤。
46 1
|
3月前
|
Arthas 监控 Java
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
本文介绍了阿里云 Java Agent 4.x 版本在基于 OTel Java Agent 二次开发过程中的实践与思考,并重点从功能、性能、稳定性、兼容性四个方面介绍了所做的工作。同时也介绍了阿里云可观测团队积极参与开源建设取得的丰厚成果。
379 11
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
|
3月前
|
XML Java 测试技术
从零开始学 Maven:简化 Java 项目的构建与管理
Maven 是一个由 Apache 软件基金会开发的项目管理和构建自动化工具。它主要用在 Java 项目中,但也可以用于其他类型的项目。
110 1
从零开始学 Maven:简化 Java 项目的构建与管理
|
3月前
|
Java Android开发
Eclipse Java 构建路径
Eclipse Java 构建路径
57 3
|
搜索推荐 Java 应用服务中间件
【Maven】IDEA部署配置Maven项目教程,IDEA配置Tomcat(2019.3.3)(2023.1.3)
当涉及到软件开发和项目管理时,使用一个可靠的构建工具是非常重要的。Maven是一个广泛使用的构建工具,它为Java项目提供了一种简化的构建过程和依赖管理。 在本文中,我们将探讨如何部署Maven并开始使用它来构建您的项目。我们将介绍所需的步骤,并向您提供一些有用的提示和建议。
【Maven】IDEA部署配置Maven项目教程,IDEA配置Tomcat(2019.3.3)(2023.1.3)
|
9月前
|
Java Maven
Maven【5】在IDEA环境中配置和使用Maven
Maven【5】在IDEA环境中配置和使用Maven
203 1
|
存储 缓存 Java
Maven超细致史上最全Maven下载安装配置教学(2022更新...全版本)建议收藏...赠送IDEA配置Maven教程
Maven超细致史上最全Maven下载安装配置教学(2022更新...全版本)建议收藏...赠送IDEA配置Maven教程
3904 0
Maven超细致史上最全Maven下载安装配置教学(2022更新...全版本)建议收藏...赠送IDEA配置Maven教程

热门文章

最新文章

推荐镜像

更多