您的位置 首页 java

Java Agent入门教程

大家好,我是杨叔。关注杨叔,每天进步一点点,了解更多测试开发技术知识!

一、什么是 java Agent

Java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包,只是启动方式和普通Jar包有所不同。

对于普通的Jar包,通过指定类的main函数进行启动。但是Java Agent并不能单独启动,必须依附在一个 Java 应用程序运行,在面向切面编程方面应用比较广泛。


Java agent 的jar包通过 jvm TI( JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改。主要功能如下:

  • 可以在加载java文件之前做拦截把 字节码 做修改
  • 可以在运行期将已经加载的类的字节码做变更

比如我们用到过的Jcoco,Arthas, chaosblade等,都有使用到Java agent技术。

二、Java Agent 开发

开发环境 : 选择 IDEA 作为编辑器, maven 进行包管理

2.1 核心逻辑

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


先简单看一下这两个方法的区别,注释上也说了

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

其中 jvm 方式,也就是说要使用这个 agent 的目标应用,在启动的时候,需要指定 jvm 参数 -javaagent:xxx.jar。然后执行 main 函数 之前,JVM 会先运行 -javaagent 所指定 jar 包内 Premain-Class 这个类的 premain 方法,即在主程序运行之前先启动运行agent。
当目标应用程序启动之后,动态attach的方式启动agent,这时候就可以使用 attach 方式来使用。

2.2 打包

上面一个简单 SimpleAgent 就把我们的 Agent 的核心功能写完了(就是这么简单),接下来需要打一个 Jar 包。
通过 maven 插件,可以比较简单地输出一个合规的 java agent 包,有两种常见的使用姿势:

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


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


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

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

文件内容如下:


请注意,最后一行要有一个空行,不能少,在 idea 中,删除最后一行时,会有错误提醒:


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


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

2.3 Agent使用

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

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


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


请注意VM options的内容为之前打包的 agent 绝对地址:

 -javaagent:D:\web\agenttest\target\agen-test-1.0-SNAPSHOT-jar-with-dependencies.jar  

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


请注意上面的premain, 这个就是我们上面的SimpleAgent中的premain方法输出,在主程序main函数运行前,先运行了agent的premain函数的内容。

attach 方式

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


然后先启动目标应用程序,即运行我们的demo项目的BaseMain的main函数。然后通过jps -l获取目标应用的进程号:

将agent的AttachMain类中的目标应用进程号改成当前BaseMain的进程号:20956


运行AttachMain,将agent注入到目标应用程序,然后在demo项目的BaseMain的main函数运行控制台就可以看到agent项目SimpleAgent类下agentmain函数的代码运行了:

2.4. 进阶:替换目标程序返回内容

Demo项目中新建一个简单类,TransClass, 可以通过一个 静态方法 返回一个整数 1


我们将 TransClass 的 getNumber 方法改成返回2:


再运行main函数,得到这个返回 2 的 Java 文件编译成的类文件:TransClass.class:


将这个TransClass.class拷贝到Agent项目下:


然后Agent项目中新增类:Transformer 类:这个类实现了 ClassFileTransformer 接口


get Bytes From File 方法根据文件名读入 二进制 字符流
ClassFileTransformer 当中规定的 transform 方法则完成了类定义的替换转换:

Premain 类中, Instrumentation 的代理方法 premain下增加代码:inst.addTransformer(new Transformer())

可以看出,addTransformer 方法并没有指明要转换哪个类。 转换发生在 premain 函数执行之后,main 函数执行之前,这时每装载一个类,transform 方法就会执行一次,看看是否需要转换 ,所以,在 transform(Transformer 类中)方法中,程序用 className.equals(“TransClass”) 来判断当前的类是否需要转换。

测试验证:
Demo项目运行TransClass的main方法,会返回1:


在配置类添加JVM agent参数:


再次运行TransClass的main方法,会看到返回的是agent的内容,值变为了2:


至此,使用Java agent替换目标程序返回的内容Demo已完成

总结:以上,本次分享了:

 什么是Java agent
如何去开发一个简单的agent
开发后如何使用agent
进阶使用:如何使用agent去替换目标程序接口返回的内容  

=================================
以上,如果对你有帮助,

欢迎VX搜索关注我的公众号:程序员杨叔,带你了解更多测试相关干货内容资料!

文章来源:智云一二三科技

文章标题:Java Agent入门教程

文章地址:https://www.zhihuclub.com/195058.shtml

关于作者: 智云科技

热门文章

网站地图