java agent
jdk1.5以后引入的字节码插桩技术,可以在代码中加入切点,独立于目标程序,业务侵入性相比于普通的AOP编程要低,可以用作接口的性能检测,参数可性能监控等,常见的微服务链路跟踪的实现原理之一
jdk1.5后新增了类java.lang.instrument.Instrumentation,它提供在运行时重新加载某个类的的class文件的api,部分源码如下:
public interface Instrumentation {
void
addTransformer(ClassFileTransformer transformer, boolean canRetransform);
void
addTransformer(ClassFileTransformer transformer);
boolean
removeTransformer(ClassFileTransformer transformer);
boolean
isRetransformClassesSupported();
void
retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
通过addTransformer可以加入一个转换器,转换器可以实现对类加载的事件进行拦截并返回转换后新的字节码,通过redefineClasses或retransformClasses都可以触发类的重新加载事件。通过这几个方法的组合,就可以实现不修改原来代码并使用切片的目的
使用方式主要分这么几步:1.新建切片程序实现切片逻辑;2.程序入口由main函数改为premain;3.加入自定义转换器;4.修改原先程序启动方式,加入-javaagent参数(更多参数可以参考java --help)
bytebuddy
bytebuddy是一个可以在运行时动态生成java class的类库,无需编译器帮助,使用它的主要目的是它可以创建任意类,不限于实现用于创建运行时代理的接口,而且它提供了一种方便的API,可以使用java代理或在构建过程中手动更改类,算是功能强大而交互性好的类库,官网:https://bytebuddy.net,中文文档:https://notes.diguage.com/byte-buddy-tutorial
下面通过例子实现用java agent监控springboot程序的controller并打印交互数据
示例
新建目标程序
新建一个helloworld的springboot的web程序,由于程序简单,这里只贴出一些需要的代码
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
@PostMapping("/say")
public String say(@RequestParam("word") String word) {
return "say: " + word;
}
}
新建agent程序
新建agent程序,定义程序入口的premain方法,在MF文件中定义premain方法
public static void premain(String agentArgs, Instrumentation inst) {}
这个方法相当于普通Java程序的入口方法main,agentArgs相当于main方法里的 String[] args,但是不是数组状态,而是命令行怎么输入的,这里就怎么传递,需要手动处理,inst,就是刚刚说到的Instrumentation类,其