赞
踩
大家好,我是杨叔。每天进步一点点,关注微信公众号【程序员杨叔】,了解更多测试开发技术知识!
Java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包。只是启动方式和普通Jar包有所不同,对于普通的Jar包,通过指定类的main函数进行启动。但是Java Agent并不能单独启动,必须依附在一个Java应用程序运行,在面向切面编程方面应用比较广泛。
Java agent 的jar包通过JVMTI(JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改。主要功能如下:
开发环境: 选择 IDEA 作为编辑器,maven 进行包管理
创建一个新的项目(or 子 module),然后我们新建一个 SimpleAgent 类:
先简单看一下这两个方法的区别,注释上也说了
jvm 参数形式: 调用 premain 方法
attach 方式: 调用 agentmain 方法
其中 jvm 方式,也就是说要使用这个 agent 的目标应用,在启动的时候,需要指定 jvm 参数 -javaagent:xxx.jar。然后执行 main 函数之前,JVM 会先运行 -javaagent 所指定 jar 包内 Premain-Class 这个类的 premain 方法,即在主程序运行之前先启动运行agent。
当目标应用程序启动之后,动态attach的方式启动agent,这时候就可以使用 attach 方式来使用。
上面一个简单 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
命令打包
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函数的代码运行了:
Demo项目中新建一个简单类,TransClass, 可以通过一个静态方法返回一个整数 1
我们将 TransClass 的 getNumber 方法改成返回2:
再运行main函数,得到这个返回 2 的 Java 文件编译成的类文件:TransClass.class:
将这个TransClass.class拷贝到Agent项目下:
然后Agent项目中新增类:Transformer 类:这个类实现了 ClassFileTransformer 接口
getBytesFromFile 方法根据文件名读入二进制字符流
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去替换目标程序接口返回的内容
=========================================================
以上,如果对你有帮助,欢迎关注我的公众号
扫码关注程序员杨叔的微信公众号,带你了解更多测试相关干货内容资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。