java agent 介绍
java agent-02-Java Instrumentation API
java agent-03-Java Instrumentation 结合 bytekit 实战笔记 agent attach
java agent-03-Java Instrumentation 结合 bytekit 实战笔记 agent premain
前面几篇文档,我们简单介绍了一下 java Instrumentation。
java agent 介绍
Java Instrumentation API
本篇我们结合一下 bytekit 进行实际的文件修改。
基于 Instrumentation 来实现的有:
APM 产品: pinpoint、skywalking、newrelic、听云的 APM 产品等都基于 Instrumentation 实现
热部署工具:Intellij idea 的 HotSwap、Jrebel 等
Java 诊断工具:Arthas、Btrace 等
由于对字节码修改功能的巨大需求,JDK 从 JDK5 版本开始引入了java.lang.instrument 包。它可以通过 addTransformer 方法设置一个 ClassFileTransformer,可以在这个 ClassFileTransformer 实现类的转换。
JDK 1.5 支持静态 Instrumentation,基本的思路是在 JVM 启动的时候添加一个代理(javaagent),每个代理是一个 jar 包,其 MANIFEST.MF 文件里指定了代理类,这个代理类包含一个 premain 方法。JVM 在类加载时候会先执行代理类的 premain 方法,再执行 Java 程序本身的 main 方法,这就是 premain 名字的来源。在 premain 方法中可以对加载前的 class 文件进行修改。这种机制可以认为是虚拟机级别的 AOP,无需对原有应用做任何修改,就可以实现类的动态修改和增强。
从 JDK 1.6 开始支持更加强大的动态 Instrument,在JVM 启动后通过 Attach API 远程加载,后面会详细介绍。
Instrumentation 是 java.lang.instrument 包下的一个接口,这个接口的方法提供了注册类文件转换器、获取所有已加载的类等功能,允许我们在对已加载和未加载的类进行修改,实现 AOP、性能监控等功能。
它的 addTransformer 给 Instrumentation 注册一个 transformer,transformer 是 ClassFileTransformer 接口的实例,这个接口就只有一个 transform 方法,调用 addTransformer 设置 transformer 以后,后续JVM 加载所有类之前都会被这个 transform 方法拦截,这个方法接收原类文件的字节数组,返回转换过的字节数组,在这个方法中可以做任意的类文件改写。
Javaagent 是一个特殊的 jar 包,它并不能单独启动的,而必须依附于一个 JVM 进程,可以看作是 JVM 的一个寄生插件,使用 Instrumentation 的 API 用来读取和改写当前 JVM 的类文件。
1.在 JVM 启动的时候加载,通过 javaagent 启动参数 java -javaagent:myagent.jar MyMain,这种方式在程序 main 方法执行之前执行 agent 中的 premain 方法
2.在 JVM 启动后 Attach,通过 Attach API 进行加载,这种方式会在 agent 加载以后执行 agentmain 方法
这两个方法都有两个参数
第一个 agentArgument 是 agent 的启动参数,可以在 JVM 启动命令行中设置,比如 的情况下 agentArgument 的值为 "appId:agent-demo,agentType:singleJar"。
第二个 instrumentation 是 java.lang.instrument.Instrumentation 的实例,可以通过 addTransformer 方法设置一个 ClassFileTransformer。
第一种 premain 方式的加载时序如下:
我们本篇文章重点介绍一下第一种 premain 方式。
引入对应的依赖,并且指定编译插件,避免资源文件打包丢失。
- MyAgent.java
作为代理的入口,主要是 premain 方法。
我们在这里指定了类转换实现 MyClassFileTransformer
- MyClassFileTransformer.java
这里我们针对性的增强我们的测试类 ,
- Sample.java
- SampleInterceptor.java
对应的代码转换策略,基于 bytekit
我们需要在 文件中配置 agent 信息,便于程序启动时识别。
- MANIFEST.MF
编译 agent
编译程序
可以得到上面的 agent 包,路径为:D:/github/bytekit-learn/bytekit-learn-agent/target/bytekit-learn-agent-1.0-SNAPSHOT.jar
main 配置
我们写一个简单的 main 方法:
我们在 main 方法,配置对应的 vm 启动参数:
测试结果
测试日志如下:
https://blog.csdn.net/tterminator/article/details/54381618