0
点赞
收藏
分享

微信扫一扫

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • ​​https://github.com/liuyueyi/java-agent​​

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

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

2. 声明

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

  • 微博地址: ​​小灰灰 Blog​​
  • QQ: 一灰灰/3302797840

3. 扫描关注

一灰灰 blog

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


举报

相关推荐

0 条评论