一、arthas
上篇文章我们借助arthas监测线上系统的运行信息、排查程序运行缓慢问题,尽管这样已经非常好了,但是还是会有些问题,有时我们可能还需要查看方法中某个变量的内容,或者方法内某个计算的运行时长,通过前面讲的可能无法满足需求。
因此本片文章通过在不重启应用程序的情况下,反编译源码成java文件,并添加上自定义的打印逻辑,然后在编译回class文件加载到JVM中执行。
二、环境准备
新建一个SpringBoot项目,并写入下面的controller:
@RestController
public class FanbianyiController {
@GetMapping("/jadTest/{i}")
public String jadTest(@PathVariable Integer i) throws InterruptedException {
System.out.println("jadTest");
String t = op1(i);
return "t:" + t;
}
private String op1(int i) throws InterruptedException {
i = i++;
i=i*5;
return op2(i);
}
private String op2(int i) throws InterruptedException {
i = i*i;
TimeUnit.SECONDS.sleep(1); // 模拟耗时计算
i = i/2;
return String.valueOf(i);
}
}
上面我们写了一个jadTest接口,并调用了op1方法,然后有调用了op2方法,假如我们想知道i在op1和op2方法中每一步的值,及op2中的第一个计算的执行时间,在不重启项目的情况下,现在好像看不到。
不过通过arthas就可以实现我们上面的需求,下面我们先对FanbianyiController进行反编译。
三、反编译源码
在arthas中通过 jad 就可以查看源码,并且还有高亮效果:
jad com.bxc.arthasdemo.controller.FanbianyiController
已经可以看到源码了,不过也将ClassLoader的信息也打印出来了,可以使用–source-only 来排除掉:
jad --source-only com.bxc.arthasdemo.controller.FanbianyiController
现在就只有源码信息了,不过这还是在控制台,我们需要写到一个java文件中进行修改:
jad --source-only com.bxc.arthasdemo.controller.FanbianyiController > D:/FanbianyiController.java
执行完后就可以看到目录下已经有了FanbianyiController.java文件:
下面我们修改FanbianyiController.java文件,加上打印和计时:
下面我们就把修改后的FanbianyiController.java编译成class文件:
四、java编译为class文件:
在arthas中通过 mc 就可以将.java编译成.class文件:
mc D:/FanbianyiController.java -d D:/
上面已经打印出了文件存放的地址,可以看下:
五、将class文件加载到JVM中:
在arthas中通过 redefine 就可以将.class文件,加载到JVM中 :
注意 redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field)。reset命令对redefine的类无效。如果想重置,需要redefine原始的字节码。redefine命令和jad/watch/trace/monitor/tt等命令会冲突。执行完redefine之后,如果再执行上面提到的命令,则会把redefine的字节码重置。
redefine D:/com/bxc/arthasdemo/controller/FanbianyiController.class
上面已经加载成功了,可以再次访问jadTest接口:
观察控制台的打印:
上面就已经成功实现了生产环境反编译动态修改程序调试应用的过程了。