字节码编程,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 个
四、技术实现
1. HotSwapper 操作类热加载
德莱联盟,王牌工程师,申请出栈
/**
* 公众号: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());
}
}
2. 知识点讲解
- 多线程模拟循环调用,这个方法会一直执行查询。在后续修改类之后输出的结果信息会有不同。
javassist.tools.HotSwapper,是javassist的包中提供的热加载替换类操作。在执行时需要启用 JPDA(Java平台调试器体系结构)。ctMethod.setBody,重写方法的内容在上面两个章节已经很清楚的描述了。$1 是获取方法中的第一个入参,大括号{}里是具体执行替换的方法体。- 最后使用
hs.reload执行热加载替换操作,这里的ctClass.toBytecode()获取的是处理后类的字节码。
五、测试结果
1. 引入tools.jar
2. 配置-agentlib
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
3. 执行测试
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毛钱给了王牌工程师小傅哥。

4. 效果演示
六、总结
- 没得办法,即使再好的技术不加点段子也没人看。只能坑我兄弟飞机了!德莱联盟,王牌工程师,申请出
栈 - 关于热加载修改类的操作,在实际场景中还是蛮多的,但一般都是比较苛刻的场景诉求。在平时开发中还是比较少遇到的,并且CRUD开发不会遇到。
Javassist对ASM这样的字节码操作封装起来提供的API确实很好操作,在一些场景下也不需要考虑JVM中局部变量和操作数栈。但如果需要更高的性能,可以考虑使用ASM。
字节码编程,Javassist篇三《使用Javassist在运行时重新加载类「替换原方法输出不一样的结果」》的更多相关文章
- PHP 进阶篇:面向对象的设计原则,自动加载类,类型提示,traits,命名空间,spl的使用,反射的使用,php常用设计模式 (麦子学员 第三阶段)
以下是进阶篇的内容:面向对象的设计原则,自动加载类,类型提示,traits,命名空间,spl的使用,反射的使用,php常用设计模式 ================================== ...
- 字节码编程,Javassist篇一《基于javassist的第一个案例helloworld》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 目录 @ 目录 目录 一.前言 二.开发环境 三.案例目标 四.技术实现 五.测试结果 1. ...
- 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...
- 字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相对于小傅哥之前编写的字节码编程: ASM.Javassist 系列,Byte Bu ...
- Javassist之使用字节码在运行时生成新的类 01
介绍 Javassist是一个开源的分析.编辑和创建Java字节码的类库.是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的.它已加入了开放源代码JBoss 应用 ...
- [源码解析] 深度学习流水线并行 PipeDream(4)--- 运行时引擎
[源码解析] 深度学习流水线并行 PipeDream(4)--- 运行时引擎 目录 [源码解析] 深度学习流水线并行 PipeDream(4)--- 运行时引擎 0x00 摘要 0x01 前言 1.1 ...
- VSTO学习笔记(三) 开发Office 2010 64位COM加载项
原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...
- 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理
服务器文档下载zip格式 刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...
- [WP8.1UI控件编程]Windows Phone大数据量网络图片列表的异步加载和内存优化
11.2.4 大数据量网络图片列表的异步加载和内存优化 虚拟化技术可以让Windows Phone上的大数据量列表不必担心会一次性加载所有的数据,保证了UI的流程性.对于虚拟化的技术,我们不仅仅只是依 ...
随机推荐
- Python函数的返回值和作用域
函数的返回值和作用域 1.返回值 def guess(x): if x > 3: return "> 3" else: retu ...
- [Abp vNext 入坑分享] - 1.创建初始的项目
一.简要说明 本篇文章主要是跟着官方的文档把项目安装好先,同时了解一下大概的项目结构. 二.具体步骤 2.1全局安装ABP CLI,直接在cmd中安装即可.如果你之前安装过,这里可以略过: dotne ...
- 本地同时使用多个git账号
config文件说明 Git Document指示在首次安装git的时候需要配置Config的相关内容信息,有三个地方存储了config文件,决定了读取的场景不同. 1 /etc/gitconfig: ...
- centos7 —— 网络连接问题
今天用虚拟机(VM)安装好centos7后,发现无法连接网络,百思不得其解: 第一步:找到需要修改的文件位置,查明原因 #.查看网络是否可以ping通 ~ ping www.baidu.com #.查 ...
- iview使用之怎样通过render函数在tabs组件中添加标签
在实际项目开发中我们通常会遇到一些比较'新颖'的需求,而这时iview库里往往没有现成可用的组件示例,所以我们就需要自己动手翻阅IviewAPI进行自定义一些组件,也可以说是将iview库里的多种组件 ...
- 数值计算方法实验之newton多项式插值 (Python 代码)
一.实验目的 在己知f(x),x∈[a,b]的表达式,但函数值不便计算或不知f(x),x∈[a,b]而又需要给出其在[a,b]上的值时,按插值原则f(xi)=yi (i=0,1,……, n)求出简单函 ...
- prefetch 和 preload 及 webpack 的相关处理
使用预取和预加载是网站性能和用户体验提升的一个很好的途径,本文介绍了使用 prefetch 和 prefetch 进行预取和预加载的方法,并使用 webpack 进行实现 Link 的链接类型 < ...
- python学习笔记(四)---用户输入与while循环
用户输入 函数input demo1: message = input("all you input is chars:") print(message) demo2: 由inpu ...
- 2019-2020-1 20199325《Linux内核原理与分析》第七周作业
第七周作业 1.进程描述符task_struct数据结构(一) 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct数据结构很 ...
- 【转载】pyinstaller的使用和几个坑
1.-w是不显示命令窗口, -i 图标文件的路径 这是改变图标的,但是我发现只能改变任务栏里的和命令窗口的图标,并不能改变exe文件的图标.另外这些参数要加载pyinstaller和路径中间. 2 ...