1背景
实际工作过程中,项目部署一般是在内网中,如果发现后端代码有问题,有时候只是简单修改逻辑,改两三个文件,外网打包重传比较麻烦,如果外面再套一个几个G的基础镜像,那浪费的时间非常多,如果能在内网内直接修改代码并重新编译打包 能节省巨多时间,特别是遇到传数据按天计算的情况
2前置条件:
操作环境需要有jdk 和arthas工具
jar包含有所有依赖的第三方jar包
使用的示例为 mybatis-flex的示例代码的mybatis-flex-springboot(简单修改了pom 使用了springboot-mavne插件)项目与arthas自带的math-game.jar gitee.com/mybatis-fle...
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...其他内容省略 plugin中注释掉了原来的编译插件 改用springboot-maven插件编译出的jar包 包含所有依赖..
<plugins>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-compiler-plugin</artifactId>-->
<!-- <version>3.8.1</version>-->
<!-- <configuration>-->
<!-- <source>1.8</source>-->
<!-- <target>1.8</target>-->
<!-- <encoding>UTF-8</encoding>-->
<!-- </configuration>-->
<!-- </plugin>-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
情况1 jar包内配置文件的修改
操作之前原来jar包先备份
解决办法1
linux服务中直接 vim mybatis-flex-springboot-1.0-SNAPSHOT.jar
一般配置文件在BOOT-INF/classes目录下、pom文件在META-INFO目录下
解决方法2
或者jar -xvf mybatis-flex-springboot-1.0-SNAPSHOT.jar
解压完毕后 进入对应的目录进行修改
修改完毕后重新压缩
例如我的示例jar包解压后有三个目录BOOT-INF、META-INFO、org
压缩命令:
js
jar -cvfM0 mybatis-flex-springboot-1.0-SNAPSHOT.jar BOOT-INF/ META-INF/ org
压缩完验证下 java -jar mybatis-flex-springboot-1.0-SNAPSHOT.jar 看看包是否有问题
注:第三方依赖的jar包在BOOT-INF/lib下 如果需要修改也是先用jar -xvf解压 修改后 重新压成jar包,替换掉原来的lib目录下的jar包,然后返回上层再压出来最终的jar包,lib目录下解压生成的BOOT-INF、META-INFO在打包外层的jar包时要删掉 不然会报错

情况2 jar包内代码逻辑需要修改
向目标服务器传文件方便(本地有源码)
往目标服务器上传文件简单的话,本地可以编译的话,直接进行编译将编译完的class文件上传到服务器中,参考情况1中的第二种方法,将class文件上传到BOOT-INF/classes/具体的目录下,替换掉原来的class文件,并重新进行打包
向目标服务器传文件困难
arthas工具可反编译出代码,可以将修改的代码编译为class文件,并可利用redefine命令进行热更新。如果要使用redefine命令,对原来类的修改不能增加方法 也不能在方法中新增加参数,如果在类中修改涉及到新增方法或者在方法列表中增加参数,可以参考情况1.解决方法2 编译完成后 手动封装成jar包
redefine 的限制,详细参考下面链接
arthas.aliyun.com/doc/redefin...
使用 sc命令查看JVM已加载类信息
js
sd -d demo.MathGame
最后一行的classLoaderHash的值 我们后续需要使用 反编译代码
javascript
jad -c 70dea4e --source-only demo.MathGame > /tmp/MathGame.java
- jad命令是反编译指定已加载类的源码
- -c : 类所属 ClassLoader 的 hashcode
- --source-only:默认情况下,反编译结果里会带有
ClassLoader
信息,通过--source-only
选项,可以只打印源代码。 - demo.MathGame:要反编译的类全路径
根据需要修改代码
编译代码
- mc: 编译.java文件生.class文件, 详细使用方法参考官方文档arthas.aliyun.com/doc/mc.html
- -c:指定classloader的hash值
- -d:指定输出目录
最后将修改的class加载到jvm中
js
redefine -c 70dea4e /tmp/MathGame.class
常见问题
arthas反编译.java文件时,可能会失败,可以使用javac命令进行编译 javac -cp参数可以指定编译依赖的jar包或者class文件 如果有多个路径的情况下,在window下 多个路径以";"分割开,linux下 以":"分隔开。

js
#windows命令参数
javac -encoding UTF-8 -cp "D:\Users\fyzhang5\work\reference\learn\jarAddLibrary\lib\BOOT-INF\classes\;D:\Users\fyzhang5\work\reference\learn\jarAddLibrary\lib\BOOT-INF\lib\*" AccountController.java
js
#linux命令参数
javac -encoding UTF-8 -cp "/tmp/mybatis-flex/BOOT-INF/classes/:/tmp/mybatis-flex/BOOT-INF/lib/*" AccountController.java
编译出来.class文件,参考情况1中的方式2 重新构建jar包镜像,步骤麻烦 但是凑合能用,比搞个包一弄大半天省点劲