使用基于jvm-sandbox的对三层嵌套类型的改造
使用基于jvm-sandbox的对三层嵌套类型的改造
问题背景
先简单介绍下基于jvm-sandbox的imock工具,是Java方法级别的mock,操作就是监听指定方法,返回指定的mock内容。
jvm-sandbox 利用字节码操作和自定义类加载器的技术,将原始方法替换为模拟代码,从而在应用程序中实现方法级别的模拟。这种方法非常强大,但也需要对字节码操作、类加载机制和 JVM 内部原理有一定的理解。
公司要搭建一个方法级别的后端mock平台,因此我在imock的基础上进行二次开发进行使用。
问题描述
在mock某个三方接口的方法时遇到报错:ava.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.travelsky.angeldoe.output.PassengerFlightInfo
看样子是本来应该是JSONObject 无法转化成PassengerFlightInfo类型,通过日志排查问题,定位到报错代码。
PassengerFlightInfo passengerFlightInfo = JSON.parseObject(out
.getPassengerFlightInfoList().get(0).toString(), PassengerFlightInfo.class);
线上服务没有报错,测试mock环境报错,那么显然是数据的问题,通过Arthas追踪方法返回的bean对比发现,差异就是线上的PassengerFlightInfo是一个bean,测试的PassengerFlightInfo是一个object。差异由此出现。
那么问题的关键就在于,如何通过mock工具把object提前转成bean。
解决方案
改造mock agent工具思路:通过我们的mock-module.jar实现。
-
根据PsrInfoOutputBean初步解析returnObject,获取list中的object -
将object解析成PassengerFlightInfo,再通过反射技术将bean反射回PsrInfoOutputBean
代码实现
//针对cki特殊类型PsrInfoOutputBean
case 3:
//获取advice返回类型的类加载器
ClassLoader behaviorClassLoader = advice.getBehavior().getReturnType().getClassLoader();
//加载最外层PsrInfoOutputBean
Class<?> targetClass = behaviorClassLoader.loadClass(ro.getClassNames()[0]);
LogUtil.info2("targetClass=", targetClass.toString());
//根据目标类解析returnData
Object res1 = JSON.parseObject(ro.getReturnData(), targetClass);
//赋值保存做对比
Object res0 = res1;
LogUtil.info2("res1-before=", res1.toString());
// 通过反射获取passengerFlightInfoList
List<Object> passengerFlightInfoList = (List<Object>) targetClass.getMethod("getPassengerFlightInfoList").invoke(res1);
LogUtil.info2("passengerFlightInfoList=", passengerFlightInfoList.toString());
if (!passengerFlightInfoList.isEmpty()) {
// 获取 passengerFlightInfoList 列表中的第一个元素
Object firstPassengerFlightInfoList = passengerFlightInfoList.get(0);
LogUtil.info2("firstPassengerFlightInfoList=", firstPassengerFlightInfoList.toString());
// 将 firstFlightInfo 转换成 JSON 字符串
String firstFlightInfoJson = JSON.toJSONString(firstPassengerFlightInfoList);
// 获取第三层额外目标 Bean 类的类名,使用同一类加载器
Class<?> targetBeanClass = behaviorClassLoader.loadClass(ro.getClassNames()[2]);
LogUtil.info2("targetBeanClass=", targetBeanClass.toString());
//根据类解析成bean
Object targetBean = JSON.parseObject(firstFlightInfoJson, targetBeanClass);
LogUtil.info2("targetBean=", targetBean.toString());
// 创建一个新的passengerFlightInfoListNew 将 targetBean 添加到 passengerFlightInfoList 中
List<Object> passengerFlightInfoListNew = new ArrayList<>();
passengerFlightInfoListNew.add(targetBean);
// 设置 passengerFlightInfoList 属性回 res1
try {
// 执行反射方法,把passengerFlightInfoListNew反射回res
Method method = targetClass.getMethod("setPassengerFlightInfoList", List.class);
method.invoke(res1, passengerFlightInfoListNew);
} catch (Exception e) {
// 捕获异常并打印日志
LogUtil.info2("Error occurred while invoking method:=", e.getMessage()+"|"+e);
}
}
LogUtil.info2("前后的两个类equals吗?=", String.valueOf(res1.equals(res0)));
LogUtil.info2("res1-after=", res1.toString());
ProcessController.returnImmediately(res1);
break;
遇到的坑
外部获取的类名不能直接通过Class.forName加载,如下代码所示:
// 使用目标 Bean 类名解析 JSON 字符串成目标 Bean
Class<?> targetBeanClass = Class.forName(targetBeanClassName);
实际会报错:"message": "com.taobao.rigel.rap.model.PsrInfoOutputBean cannot be cast to com.taobao.rigel.rap.model.PsrInfoOutputBean", 原因是这两个bean虽然名字一样,但是类加载器不同,就导致bean的实际是不一样的。类是否相同可以用equals进行判断。
因此正确的做法是,先获取advice返回类型的类加载器,然后加载我们所需要的类,这样业务的代码就会认得我们的bean了。
//获取advice返回类型的类加载器
ClassLoader behaviorClassLoader = advice.getBehavior().getReturnType().getClassLoader();
//加载最外层PsrInfoOutputBean
Class<?> targetClass = behaviorClassLoader.loadClass(ro.getClassNames()[0]);
题外话:
为啥出现了这个错误?
出现这个报错和开发的强转类型也有关系,本地做了个小测试,同样的数据。(但咱也没发改开发的代码,只能提提建议。 = =)
1、当前异常转化:按照开发业务代码中的list强转对象
List<Object> list = JSON.*parseArray*(jsonString); PassengerFlightInfo passengerFlightInfo = (PassengerFlightInfo) list.get(0);这是使用强制类型转换的方式,直接将
list中的第一个元素强制转换为PassengerFlightInfo对象。这种方式在编译时不会报错,但如果list中的第一个元素不是PassengerFlightInfo对象,则会在运行时抛出ClassCastException异常。
2、正常转化:优化过后用toJavaObject方法
PassengerFlightInfo passengerFlightInfo = ((JSONObject) list.get(0)).toJavaObject(PassengerFlightInfo.class);: 这是使用 FastJSON 提供的toJavaObject方法,将JSONObject类型转换为PassengerFlightInfo对象。这种方式在运行时会检查转换是否可行,如果JSONObject不包含PassengerFlightInfo的属性或结构不匹配,会抛出异常。这种方式更安全,因为它提供了更多的转换检查。
推荐使用第二种方式,因为它更加健壮和安全,能够更好地处理可能出现的异常情况,并提供更好的错误信息。
- END -本文由 mdnice 多平台发布
相关文章:
使用基于jvm-sandbox的对三层嵌套类型的改造
使用基于jvm-sandbox的对三层嵌套类型的改造 问题背景 先简单介绍下基于jvm-sandbox的imock工具,是Java方法级别的mock,操作就是监听指定方法,返回指定的mock内容。 jvm-sandbox 利用字节码操作和自定义类加载器的技术,将原始方法…...
[HDLBits] Mt2015 q4b
Circuit B can be described by the following simulation waveform: Implement this circuit. module top_module ( input x, input y, output z );//001 100 010 111assign z(xy); endmodule...
C++:堆排序
堆排序 输入一个长度为n的整数数列,从小到大输出前m小的数 输入格式 第一行包含整数n和m 第二行包含n个整数,表示整数数列 输出格式 共一行,包含m个整数,表示整数数列中前m小的数 数据范围 1 ≤ m ≤ n ≤ 1 0 5 1\le m\le …...
Grafana Prometheus 通过JMX监控kafka
第三方kafka exporter方案 目前网上关于使用Prometheus 监控kafka的大部分资料都是使用一个第三方的 kafka exporter,他的原理大概就是启动一个kafka客户端,获取kafka服务器的信息,然后提供一些metric接口供Prometheus使用,随意它…...
vue项目切换页面白屏不显示解决方案
问题描述 1、页面切换后白屏,同时切换回上一个页面同样白屏 2、刷新后正常显示 3、有警告:Component inside <Transition> renders non-element root node that cannot be animated 解决方法 <Transition>中的组件呈现不能动画化的非元素…...
Goland报错 : Try to open it externally to fix format problem
这句报错的意思也就是 : 尝试在外部打开以解决格式问题 解决方案 : 将图片格式该为.png格式,再粘贴进去就可以了! 改变之后的效果 : 那么,这样就ok了...
Python-OpenCV中的图像处理-几何变换
Python-OpenCV中的图像处理-几何变换 几何变换图像缩放图像平移图像旋转仿射变换透视变换 几何变换 对图像进行各种几个变换,例如移动,旋转,仿射变换等。 图像缩放 cv2.resize() cv2.INTER_AREAv2.INTER_CUBICv2.INTER_LINEAR res cv2.r…...
前端JavaScript入门-day08-正则表达式
(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 介绍 语法 元字符 边界符 量词 字符类: 修饰符 介绍 正则表达式(Regular …...
ML类CFAR检测器在不同环境中检测性能的分析
摘要:该文是楼主翻阅书籍以及一些论文总结出来的关于ML(均值)类CFAR检测器在不同环境中的性能对比,以及优缺点的总结,可以帮助大家面对不同情形如何选择CFAR问题。由于楼主见识短浅,文中难免出现不足之处,望各位指出。…...
element-ui 路由动态加载功能
第一步 自定义默认的静态路由像登陆和首页这些一般开放的页面,主要代码如下: const router new Router({routes: [{path: "/login/index",component: () > import("../components/page/login/index.vue"),meta: {title: "登录",k…...
(学习笔记-进程管理)进程调度
进程都希望自己能够占用CPU进行工作,那么这涉及到前面说过的进程上下文切换。 一旦操作系统把进程切换到运行状态,也就意味着该进程占用着CPU在执行,但是操作系统把进程切换到其他状态的时候,就不能在CPU中执行了,于是…...
十分钟python入门 正则表达式
正则常见的三种功能,它们分别是:校验数据的有效性、查找符合要求的文本以及对文本进行切割和替换等操作。 1.元字符 所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符 元字符大致分成这几类:表示单个特殊字符的,表示…...
关于数据拷贝赋值方法
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言一、关于数据拷贝赋值方法1、最基础数据类型的变量才可以直接拷贝赋值2、自己定义的大数据类型或者时类实例化的对象不可以直接拷贝赋值1、方法一:…...
Effective Java笔记(32)谨慎并用泛型和可变参数
故事的小黄花 从出生那年就飘着 童年的荡秋千 随记忆一直晃到现在 可变参数( vararg ) 方法(详见第 53 条)和泛型都是在 Java 5 中就有了,因此你可能会期待它们可以良好地相互作用;遗憾的是,它们…...
数据结构——双向链表
双向链表实质上是在单向链表的基础上加上了一个指针指向后面地址 单向链表请参考http://t.csdn.cn/3Gxk9 物理结构 首先我们看一下两种链表的物理结构 我们可以看到:双向在单向基础上加入了一个指向上一个地址的指针,如此操作我们便可以向数组一样操作…...
Declare 关键字在 TypeScript 中如何正确使用?
如果您编写 TypeScript 代码的时间足够长,您就已经看到过declare关键字。但它有什么作用,为什么要使用它? declare关键字告诉 TypeScript 编译器存在一个对象并且可以在代码中使用。 本文解释了声明关键字并通过代码示例展示了不同的用例。 定义 在 TypeScript 中,decl…...
ChatGPT将会成为强者的外挂?—— 提高学习能力
目录 前言 一、提高学习力 🧑💻 1. 快速找到需要的知识 2. 组合自己的知识体系 3. 内化知识技能 二、提问能力❗ 三、思维、创新能力 🌟 1. 批判性思维 1.1 八大基本结构进行批判性提问 1.2 苏格拉底的提问分类方法 2. 结构化思…...
AUTOSAR规范与ECU软件开发(基础篇)1.3 车用控制器软件标准(从OSEK到AUTOSAR)
目录 AUTOSAR的前世与今生 1.1~1.3篇幅小结 AUTOSAR的前世与今生 为了迎合汽车高精度、 高实时性、 高可靠性控制的需要, 嵌入式实时操作系统(Real Time Operating System, RTOS) 逐渐在ECU中使用。与此同时, 由于不同实时操作系统间应用程序接口(Application Programmi…...
R语言5_安装Giotto
环境Ubuntu22/20, R4.1. 已开启科学上网。 第一步,更新服务器环境,进入终端,键入如下命令, apt-get update apt install libcurl4-openssl-dev libssl-dev libxml2-dev libcairo2-dev libgtk-3-dev libhdf5-dev libmagick9-dev …...
centos按用户保存历史执行命令
centos7 按用户记录历史命令的方法 在/etc/profile文件中添加以下代码。 添加完成后执行source /etc/profile 用户重新登录即可发现history被清空了。这时可以去看/usr/share/.history文件夹,该文件夹保存了所有用户每次登录所执行过的的操作记录。 文件路径为 /usr…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...
