Java Agent入门教程

大家好,我是杨叔。每天进步一点点,关注微信公众号【程序员杨叔】,了解更多测试开发技术知识!

一、什么是Java Agent

Java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包。只是启动方式和普通Jar包有所不同,对于普通的Jar包,通过指定类的main函数进行启动。但是Java Agent并不能单独启动,必须依附在一个Java应用程序运行,在面向切面编程方面应用比较广泛。
Java agent 的jar包通过JVMTI(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 接口
在这里插入图片描述
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去替换目标程序接口返回的内容

=========================================================
以上,如果对你有帮助,欢迎关注我的公众号

扫码关注程序员杨叔的微信公众号,带你了解更多测试相关干货内容资料:
在这里插入图片描述

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值