-
Notifications
You must be signed in to change notification settings - Fork 3.8k
2020 04 21 字节码编程,Javassist篇三《使用Javassist在运行时重新加载类「替换原方法输出不一样的结果」》
作者:小傅哥
博客:https://bugstack.cn
沉淀、分享、成长,让自己和他人都能有所收获!
通过前面两篇 javassist
的基本内容,大体介绍了;类池(ClassPool)、类(CtClass)、属性(CtField)、方法(CtMethod),的使用方式,并通过创建不同类型的入参出参方法,基本可以掌握如何使用这样的代码结构进行字节码编程。
那么,今天我们尝试使用 javassist
去修改一个正在执行中的类里面的方法内容。也就是在运行时重新加载类信息
可能在你平时的 CRUD 开发中并没有想到过这样的 烧操作,但它却有很多的应用场景在使用,例如;
- 热部署常用在生产环境中,主要由于这样的系统不能频繁启停且启动耗时较长的应用。
- 另外一些组件化风控模型包,给外部使用。当模型包进行升级时并不需要外部重新部署,甚至不需要让你知道升级了。
- 再者会用于开发、调试中,可以非常有效的提升编码效率,解放码农的右手和左手。
人的大脑很难创造未知的事物,所以需要学习。请多看小傅哥的码文,少搞CRUD
关于字节编程中所有涉及的代码,都可以通过关注公众号
:bugstack虫洞栈,回复:源码,进行获取。
- JDK 1.8.0
- jdk1.8.0_161\lib\tools.jar - 需要使用到
jdi
包 - javassist 3.12.1.GA
为了让案例目标更具色彩
,我们模拟一个谢飞机老婆,通过系统查询自己男朋友前女友数量
的 危机 方法,需要紧急处理的过程。
为了保障家庭的和谐化解危机,我们通过动态重新加载类,将谢飞机前女友数量修改为0
并返回。依次安定家庭和谐。最终谢飞机会给我钱,当做报酬
让谢飞机很慌的方法
public class ApiTest {
public String queryGirlfriendCount(String boyfriendName) {
return boyfriendName + "的前女友数量:" + (new Random().nextInt(10) + 1) + " 个";
}
}
可预见的结果;
你到底几个前女友!!!
谢飞机的前女友数量:3 个
谢飞机的前女友数量:5 个
谢飞机的前女友数量:8 个
德莱联盟,王牌工程师,申请出栈
/**
* 公众号:bugstack虫洞栈
* 博客栈:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
* 本专栏是小傅哥多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!
*/
public class GenerateClazzMethod {
public static void main(String[] args) throws Exception {
ApiTest apiTest = new ApiTest();
System.out.println("你到底几个前女友!!!");
// 模拟谢飞机老婆一顿查询
new Thread(() -> {
while (true){
System.out.println(apiTest.queryGirlfriendCount("谢飞机"));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 监听 8000 端口,在启动参数里设置
// java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
HotSwapper hs = new HotSwapper(8000);
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(ApiTest.class.getName());
// 获取方法
CtMethod ctMethod = ctClass.getDeclaredMethod("queryGirlfriendCount");
// 重写方法
ctMethod.setBody("{ return $1 + \"的前女友数量:\" + (0L) + \" 个\"; }");
// 加载新的类
System.out.println(":: 执行HotSwapper热插拔,修改谢飞机前女友数量为0个!");
hs.reload(ApiTest.class.getName(), ctClass.toBytecode());
}
}
- 多线程模拟循环调用,这个方法会一直执行查询。在后续修改类之后输出的结果信息会有不同。
-
javassist.tools.HotSwapper
,是javassist
的包中提供的热加载替换类操作。在执行时需要启用 JPDA(Java平台调试器体系结构)。 -
ctMethod.setBody
,重写方法的内容在上面两个章节已经很清楚的描述了。$1 是获取方法中的第一个入参,大括号{}
里是具体执行替换的方法体。 - 最后使用
hs.reload
执行热加载替换操作,这里的ctClass.toBytecode()
获取的是处理后类的字节码。
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
Listening for transport dt_socket at address: 8000
你到底几个前女友!!!
谢飞机的前女友数量:3 个
谢飞机的前女友数量:5 个
谢飞机的前女友数量:8 个
:: 执行HotSwapper热插拔,修改谢飞机前女友数量为0个!
谢飞机的前女友数量:4 个
谢飞机的前女友数量:5 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
谢飞机的前女友数量:0 个
...
Process finished with exit code -1
当看到前女友数量为 0 时,谢飞机露出了羞涩的微笑,并兑现了承诺,将4毛钱给了王牌工程师小傅哥
。
- 没得办法,即使再好的技术不加点段子也没人看。只能坑我兄弟飞机了!德莱联盟,王牌工程师,申请出
栈
- 关于热加载修改类的操作,在实际场景中还是蛮多的,但一般都是比较苛刻的场景诉求。在平时开发中还是比较少遇到的,并且CRUD开发不会遇到。
-
Javassist
对ASM
这样的字节码操作封装起来提供的API
确实很好操作,在一些场景下也不需要考虑JVM
中局部变量和操作数栈。但如果需要更高的性能,可以考虑使用ASM
。
小傅哥(微信:fustack),公众号:bugstack虫洞栈
| bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
🌏 知识星球:码农会锁
实战项目:「DDD+RPC分布式抽奖系统
」、专属小册、问题解答、简历指导、架构图稿、视频课程
🐲 头条
-
💥
🎁 Lottery 抽奖系统
- 基于领域驱动设计的四层架构的互联网分布式开发实践 -
小傅哥的《重学 Java 设计模式》
- 全书彩印、重绘类图、添加内容 -
⭐小傅哥的《Java 面经手册》
- 全书5章29节,417页11.5万字,完稿&发版 -
小傅哥的《手撸 Spring》
- 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理 -
🌈小傅哥的《SpringBoot 中间件设计和开发》
- 小册16个中间件开发30个代码库
⛳ 目录
💋 精选
🐾 友链
建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。
作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈
2021年10月24日,小傅哥
的文章全部开源到代码库 CodeGuide
中,与同好同行,一起进步,共同维护。
这里我提供 3 种方式:
-
提出
Issue
:在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交PR
)。 -
处理
Issue
: 帮忙处理一些待处理的Issue
。 -
提交
PR
: 对于错别字/笔误这类问题可以直接提交PR
,无需提交Issue
确认。
详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹
- 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
- 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。