使用基于jvm-sandbox的对三层嵌套类型的改造

news2024/11/27 10:11:37

使用基于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。差异由此出现。

image-20230810214520907
image-20230810214520907
image-20230810231158842
image-20230810231158842

那么问题的关键就在于,如何通过mock工具把object提前转成bean。

解决方案

改造mock agent工具思路:通过我们的mock-module.jar实现。

  1. 根据PsrInfoOutputBean初步解析returnObject,获取list中的object
  2. 将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 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【RabbitMQ与SpringBoot集成测试收发消息】

【RabbitMQ与SpringBoot集成测试收发消息】 一、环境说明二、实验步骤三、小结 一、环境说明 安装环境&#xff1a;虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8RabbitMQ版本&#xff1a;rabbitmq-server-3.8.8-1.el7.noarch.rpm编程工具Idea 运行JDK为17 二、实验步骤 在Rab…

无货源多功能跨境商城智能搭建--软件制作+程序开发

随着全球电子商务的快速发展&#xff0c;跨境贸易已经成为一种新的商业趋势。在这个背景下&#xff0c;建立一个多功能跨境人工智能商城数据处理平台&#xff0c;不仅可以提高跨境电商的运营效率&#xff0c;还可以增强用户的购物体验&#xff0c;进一步推动跨境贸易的发展。本…

LinuxC编程——进程

目录 一、概念1.1 程序1.2 进程 二、特点⭐⭐⭐三、进程段四、进程分类五、进程状态六、进程状态转换图七、函数接口1. 创建子进程2. 回收进程资源3. 退出进程4. 获取进程号 八、守护进程 一、概念 进程和程序是密不可分的两组概念&#xff0c;相对比&#xff0c;便于理解。 1.…

春秋云镜 CVE-2022-21661

春秋云镜 CVE-2022-21661 wordpress < 5.8.3 存在sql注入漏洞 靶标介绍 2022年1月6日&#xff0c;wordpress发布了5.8.3版本&#xff0c;修复了一处核心代码WP_Query的sql注入漏洞。WP_Query是wordpress定义的一个类&#xff0c;允许开发者编写自定义查询和使用不同的参数…

安装Ubuntu,创建分区时无EFI系统分区选项的解决方法

电脑型号&#xff1a;联想小新Air2021 操作系统&#xff1a;windows10 硬盘&#xff1a;一块512G固态 如题&#xff0c;安装Ubuntu&#xff0c;在创建分区时&#xff0c;“用于”栏 没有“EFI系统分区”选项&#xff0c;导致在重启电脑后&#xff0c;发现直接进了Windows系统…

STM32芯片的内部架构介绍

STM32芯片由内核和片上外设两部分组成。STM32F103采用Cortex-M3内核&#xff0c;该内核由ARM公司设计。芯片生产厂商ST则负责在内核之外设计部件并生产整个芯片。这些内核之外的部件被称为核外外设或片上外设&#xff0c;如GPIO、USART&#xff08;串口&#xff09;、I2C、SPI等…

Cookie和Session详解及区别

目录 1&#xff0c;会话跟踪技术的概述 2&#xff0c;Cookie 2.1 Cookie的基本使用 1.概念 2.Cookie的工作流程 3.Cookie的基本使用 3.1 发送Cookie 3.2 获取Cookie 3.3设置Cookie存活时间 3&#xff0c;Session 3.1 Session的基本使用 3.2Session销毁 4.cookie和s…

plt绘制实测值与预测值的散点图

# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt from matplotlib.pyplot import MultipleLocator""" 输出观测值和模型预测值之间的拟合曲线,即拟合模型 决定系数R2&#xff0c;有拟合曲线公式&#xff0c;有1:1线 ""&…

笔记顶顶顶顶

这里写目录标题 基础**1. 说下计算机网络体系结构****2. OSI七层模型各自的功能是什么&#xff1f;**3. **说一下每一层对应的网络协议有哪些&#xff1f;**4. 那么数据在各层之间是怎么传输的呢&#xff1f; 应用层5. 从浏览器地址栏输入 url 到显示主页的过程&#xff1f;6. …

【24择校指南】南京大学计算机考研考情分析

南京大学(A) 考研难度&#xff08;☆☆☆☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分数人数统计&#xff09;、院校概况、23初试科目、23复试详情、参考书目、各科目考情分析、各专业考情分析。 正文2178字&#xff0c;预计阅读&#xff1a;6分…

计算机基础知识二

1、八进制 逢八进一。用8个计数符号表示&#xff0c;0、1、2、3、4、5、6、7。 2、八进制转二进制 规则&#xff1a;3位二进制转换1位八进制数。1位八进制数转换3位二进制数。 例如&#xff1a; 10011101B , 0.1101010011B 转换成八进制数。 3、十六进制 用16个计数符号0、1、…

【刷题笔记8.10】LeetCode题目:有效括号

LeetCode题目&#xff1a;有效括号 1、题目描述&#xff1a; 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同…

深度学习阶段性回顾

本文针对过去两周的深度学习理论做阶段性回顾&#xff0c;学习资料来自吴恩达老师的2021版deeplearning.ai课程&#xff0c;内容涵盖深度神经网络改善一直到ML策略的章节。视频链接如下&#xff1a;吴恩达深度学习视频链接 &#xff08;注&#xff1a;本文出自深度学习初学者&a…

SAP ABAP 批量CC01 创建ECN的物料和BOM 清单(RFC: CCAP_ECN_CREATE)

效果如下&#xff1a; 源代码&#xff1a; *&---------------------------------------------------------------------* *& Report ZCYCLE054 *&---------------------------------------------------------------------* *&批量CC01 *&----------------…

docker的服务/容器缺少vim问题

背景/问题&#xff1a; docker的服务/容器缺少vim问题 在docker的mysql服务中安装Vim 1、执行apt-get update root6d8d17e320a0:/# apt-get update问题:文件下载失败 Err:1 http://security.debian.org/debian-security buster/updates InRelease Temporary failure resolv…

MyBatis的XML配置文件

Mybatis的开发有两种方式&#xff1a; 注解 XML配置文件 通过XML配置文件的形式来配置SQL语句&#xff0c;这份儿XML配置文件在MyBatis当中也称为XML映射文件。 导学&#xff1a;在MyBatis当中如何来定义一份儿XML映射文件&#xff1f; 在MyBatis当中&#xff0c;定义XML…

uniapp文件下载并预览

大概就是这样的咯&#xff0c;文件展示到页面上&#xff0c;点击文件下载并预览该文件&#xff1b; 通过点击事件handleDownLoad(file.path)&#xff0c;file.path为文件的地址&#xff1b; <view class"files"><view class"cont" v-for"(…

MD-MTSP:光谱优化算法LSO求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、光谱优化算法LSO 光谱优化算法&#xff08;Light Spectrum Optimizer&#xff0c;LSO&#xff09;由Mohamed Abdel-Basset等人于2022年提出。 参考文献&#xff1a; [1]Abdel-Basset M, Mohamed R, Sallam KM, Chakrabortty RK. Light Spectrum Optimizer: A Novel Physi…

数据结构 — 时间复杂度、空间复杂度

前言 数据结构_空间复杂度_时间复杂度讲解_常见复杂度对比 本文介绍数据结构中的时间复杂度和空间复杂度 ***文章末尾&#xff0c;博主进行了概要总结&#xff0c;可以直接看总结部分*** 博主博客链接&#xff1a;https://blog.csdn.net/m0_74014525 点点关注&#xff0c;后期…

STL文件格式详解【3D】

STL&#xff08;StereoLithography&#xff1a;立体光刻&#xff09;文件是 3 维表面几何形状的三角形表示。 表面被逻辑地细分或分解为一系列小三角形&#xff08;面&#xff09;。 每个面由垂直方向和代表三角形顶点&#xff08;角&#xff09;的三个点来描述。 切片算法使用…