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

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

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

本篇将作为 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 中,删除最后一行时,会有错误提醒

然后我们的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 参数,如下

请注意上面红框的内容为上一节打包的 agent 绝对地址: -javaagent:/Users/..../target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar

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

请注意上面的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获取目标应用的进程号

当上面的 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 被成功注入进去了

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. 源码

1. 一灰灰 Blog: https://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰 blog

原文地址:https://www.cnblogs.com/yihuihui/p/12503020.html

时间: 2024-10-10 12:33:04

IDEA + maven 零基础构建 java agent 项目的相关文章

maven构建java web项目(eclipse原理同)

maven构建java web项目 1.  mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp 如果是用eclipse,则可以新建webapp的maven工程.以下同. 2. 再pom.xml中加入 <build> <finalName>gswm-web</finalName> <plugins> <plugin> <groupId>org.apach

【JAVA学习笔记】-零基础学习JAVA人分享自己的心得(转)

进入毕业季,毕业设计早早的做完,不想那么早进入职场,便趁此大好时光,一边接受企业培训,一遍自学JAVA.在我看来,学习一门语言必备的几个要点在于,看,练,悟.在这个连技术也已经沦为快餐的时代,很多人无可厚非的认为,在短时间内,快速应用一门语言才是他们所追求的,这也造成了当今培训机构的泛滥.我对此不评价,存在既是合理.但是,我这些心得体会,是想对那些真正喜欢技术的朋友,动机纯粹的朋友而言的,那些不喜欢的人,尽管拍砖,嘿嘿. 学习技术的时候,你是否总会被一些琐事烦恼呢,qq,网游,小说... 我并不

使用maven, myeclipse工具构建spring mvc项目

一.使用myeclipse 创建一个新的 maven项目. (ps:1.在filter过滤的时候输入 webapp 选择"maven-archetype-webapp". 2.在main下建一个java文件夹(建source folder可能不能成功)) 具体可参考:http://www.cnblogs.com/waniu/p/3798775.html 二.将project 转变成webproject.(右键--properties--myeclipse--project facets

JAVA学习_零基础学习JAVA方法_零基础学习JAVA思路

在学习JAVA前,对于一个真正初学者(也就是从零开始学习JAVA人员)的一个问题:什么是JAVA,然后才是怎么学习JAVA?JAVA是sun microsystems在1995年推出的高级编程语言,其分为Java SE.Java EE.Java ME三大体系,而JAVA SE是JAVA的基础,继JAVASE之后是JAVAEE,JAVA ME.JAVASE是JAVAEE的基础,Servlet.JSP是框架的基础.数据库方面个人建议学习oracle,当然其它的像 MySQL,MSSQL,都可以,只要

www808888webcom零基础学Java怎么开始?199O883661学习哪些内容?

零基础学Java怎么开始?Java要学习哪些内容?攻城狮之友 2018-11-12 17:13:34Java 语言是一门随时代迅速发展的计算机语言程序,其深刻展示了程序编写的精髓,加上其简明严谨的结构及简洁的语法编写为其将来的发展及维护提供了保护.那么零基础学Java怎么开始?Java要学习哪些内容呢? 头一阶段的Java基础 JavaEE的学习内容从计算机基本概念,DOS命令开始,入门编程语言扫盲,什么是程序,如何配置Java开发环境,Java编程的过程是怎样的,Java有什么物特点,程序是如

五本书籍助你从零基础学习java编程到精通之路

前天的文章给大家介绍了Java初学者应该学习哪些基础,亦是美网络小编从Java的三个大方向:J2SE.J2EE.J2ME对大家做了前景分析,这两天也收到了大家的很多反馈,很多小伙伴看了后对自己的Java编程方向有了比较明确的目标,也有小伙伴们说,要是能提供一些Java初学者应看的书籍就更好了,其实推荐Java书籍已经被规划在小编写的Java编程系列文章之中,常言道:书中自有黄金屋,书中自有颜如玉,下面亦是美网络小编给大家推荐的Java书籍中虽然没有黄金屋和颜如玉,但却能让Java编程的初学者快速

零基础学习java:变 量(一)

一.变量的概念: (1)内存中的一个存储区域 (2)该区域有自己的名称(变量名)和类型(数据类型) (3)Java中每个变量必须先声明,后使用 (4)该区域的数据可以在同一类型范围内不断变化 使用变量注意: (1)变量的作用域:一对{ }之间有效 (2)初始化值 定义变量的格式:数据类型    变量名  =  初始化值 二.变量的分类--按数据类型 对于每一种数据都定义了明确的具体数据类型,在内存中分配了不同大小的内存空间. 1.整数类型:byte.short.int.long Java各整数类

音乐出身的妹纸,零基础学习JAVA靠谱么

问:表示音乐出身的妹纸一枚  某一天突然觉得身边认识的是一群程序员   突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Java去..这是不是一条不归路 ... 答:初级应用编程没什么难的 , 只要数学方面不是特别渣, 思维逻辑不混乱,基本上是可行的, 至于其他的 ,没什么可考虑的 , 听得再多,不如向写一波程序 . 你的担忧不无道理,人最怕的就是失去热情.老师的发展空间小且多数属于事业单位 ,在选择和个人成长性上可能较

零基础完成网易MIni项目实战

零基础完成网易MIni项目实战 下载链接:https://www.yinxiangit.com/112.html 原文地址:https://www.cnblogs.com/bingerger/p/11629043.html