1 思路
- 1.在出问题的服务器上部署一个
arthas
,并启动。 - 2.
jad --source-only
类全限定名 > 目录/文件名.java
。jad
命令反编译,然后可以用其它编译器,比如vim
来修改源码。 - 3.修改错误代码。
-
- 查看类加载器的
hashcode
,sc -d
类全名。
- 查看类加载器的
- 5.
mc-c
类加载器的hashcode
目录/文件名.java -d
输出目录mc
命令用来编译修改过的代码。 - 6.
retransform class
文件所在目录/xxx.class
用retransform
命令加载新的字节码。
2 实操
- 这里用
jdk17
启动项目和arthas
/opt/jdk17/jdk-17.0.10/bin/java -jar /opt/test-spi.jar
/opt/jdk17/jdk-17.0.10/bin/java -jar arthas-boot.jar
选择PID
1
[INFO] arthas home: /opt/arthas
[INFO] Try to attach process 9088
Picked up JAVA_TOOL_OPTIONS:
[INFO] Attach process 9088 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.7.2
main_class
pid 9088
time 2024-04-09 09:38:38
- 反编译要修改的类的源码
jad --source-only com.logicfeng.testspi.controller.TestSpiController > /opt/testSpi/TestSpiController.java
- 因为编译一个类可能需要其他
import
的类,此时可先查找下这个类的加载器的hash
码:sc -d类全名
[arthas@9088]$ sc -d com.logicfeng.testspi.controller.TestSpiController
class-info com.logicfeng.testspi.controller.TestSpiController
code-source nested:/opt/test-spi.jar/!BOOT-INF/classes/!/
name com.logicfeng.testspi.controller.TestSpiController
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name TestSpiController
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.launch.LaunchedClassLoader@5305068a
+-jdk.internal.loader.ClassLoaders$AppClassLoader@46fbb2c1
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@48b070d8
classLoaderHash 5305068a
- 编译修改后的类
[arthas@9088]$ mc -c 5305068a /opt/testSpi/TestSpiController.java -d /opt/testSpi
Memory compiler output:
/opt/testSpi/com/logicfeng/testspi/controller/TestSpiController.class
Affect(row-cnt:1) cost in 1164 ms.
- 让类加载器重新加载新编译的字节码,加载新编译的
.class
文件,retransform
到JVM
里
[arthas@9088]$ retransform /opt/testSpi/com/logicfeng/testspi/controller/TestSpiController.class
retransform success, size: 1, classes:
com.logicfeng.testspi.controller.TestSpiController
jad
命令查看
[arthas@9088]$ jad com.logicfeng.testspi.controller.TestSpiController
ClassLoader:
+-org.springframework.boot.loader.launch.LaunchedClassLoader@5305068a
+-jdk.internal.loader.ClassLoaders$AppClassLoader@46fbb2c1
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@48b070d8
Location:
nested:/opt/test-spi.jar/!BOOT-INF/classes/!/
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* org.springframework.web.bind.annotation.GetMapping
* org.springframework.web.bind.annotation.RequestMapping
* org.springframework.web.bind.annotation.RestController
*/
package com.logicfeng.testspi.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value={"/test/spi"})
public class TestSpiController {
@GetMapping(value={"/name"})
public String testSpi() {
/*20*/ String string = "lisi";
/*21*/ return string;
}
}
Affect(row-cnt:1) cost in 70 ms.
- 测试
[root@logicfeng ~]# curl http://localhost:8032/test/spi/name
zhangsan[root@logicfeng ~]# curl http://localhost:8032/test/spi/name
[root@logicfeng ~]# curl http://localhost:8032/test/spi/name
lisi[root@logicfeng ~]#
3 注意事项
- 1、程序重启之后,字节码文件会恢复,除非将
class
文件放入jar
包中进行更新。 - 2、使用
retransform
不能添加方法或者字段! - 3、不能更新正在执行中的方法!