IMLC.ME

巧用类文件替换加速 DEBUG

相比你们肯定遇到不停改代码试错和定位问题。最朴素的方法,就是直接修改项目代码,重新构建和打包,并部署到测试环境。但是,如果你的项目是个中大型项目,启动流程漫长,触发代码分支繁琐,那就很让人烦躁。

脑补一下这样的流程:

  1. 项目构建,耗时5分钟
  2. 打包,耗时3分钟
  3. 上传到仓库系统,耗时3分钟
  4. 从测试环境拉取刚上传的 SNAPSHOT ,耗时10秒
  5. 修改版本,重新启动应用程序,耗时3分钟
  6. 手工操作,重现问题,耗时1分钟

这样加起来,10~15分钟就没了。虽然对于上班摸鱼的我们来说,问题不大。但是总有要赶项目进度的时候不是?

而对于上述场景,有相当多的时候只是为了加一行日志,调整一下标志位,或者修改一下方法参数。重新走一遍发布流程实在事倍功半。或许你需要的,只是简单替换一下类文件。

总所周知,.java 源代码经过编译后,生成 .class 字节码文件。JVM 读取 .class 文件,通过 ClassLoader 加载到内存中并执行。因此,要实现类替换,无非是替换 .class 文件,或者替换已经被加载到内存中的类。前者为静态替换,后者就是动态替换,或者说热替换。

静态替换

.tar.gz

.tar.gz方式打包的 Java 程序,运行时需要先解压后执行。

.tar.gz 只是一个普通的压缩包文件。通过 tar -zxvf xxx.tar.gz 解压,会获得一堆 .class 文件。虽然不同的打包工具、不同的公司内部的实现,会有不同的打包方法。解压后获得的文件组织很可能会有所差异。但是最后,也不过是 java <Main.class> <arguments>。java 命令行程序会加载所有类文件,并且启动指定类中的入口方法。

//TODO: demo

uber jar

Uber jar,又称 fat jar。其本质是一个以特定形式组织的 zip 压缩包。通过命令 unzip xxx.jar 解压后,也会获得一堆 .class 文件。替换完类文件后,通过命令 zip -r xxx.jar <files> 重新打包成 jar 包,就可以直接执行。

// TODO: demo

动态替换/热替换

JDB
Arthas

这里首推阿里巴巴开源的 Arthas 工具。Arthas 提供了 redefine 命令,读取外部的 .class 文件,加载并替换内存中的类。

// TODO: demo

IntelliJ IDEA

https://www.jetbrains.com/help/idea/altering-the-program-s-execution-flow.html#reload_classes

// TODO: can remote debug work?

Spring-Loaded

https://github.com/spring-projects/spring-loaded

DCEVM

https://dcevm.github.io/

Spring Hot Swapping

https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/howto-hotswapping.html

反编译与重新编译
通过构建工具重新编译源代码
通过命令行反编译和重新编译
通过 Arthas 反编译和重新编译

类替换的限制

参考文献

Instrumentation#redefineClasses

Arthas - redefine