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

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.rhkb.cn/news/89118.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【jvm】类加载子系统

目录 一、图二、类加载器作用三、类加载器角色四、类的加载过程4.1 加载4.1.1 说明4.1.2 加载.class文件的方式 4.2 链接4.2.1 验证(verify [ˈverɪfaɪ])4.2.2 准备(prepare)4.2.3 解析(resolve) 4.3 初始化4.3.1 说明4.3.2 图示14.3.3 图示24.3.3 图示3 一、图 二、类加载器…

解密Flink的状态管理:探索流处理框架的数据保留之道,释放流处理的无限潜能!

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 文章目录 一、什么是状态二、应用场景三、Flink中状态的分类四、算子状态1. 列表状态&#xff08;List State&#xff09;2. 广播状态&#xff08;Broadcast State&#xff09; 五、键控状态1. Val…

股票指数——RSI指数

RSI指数的计算非常简单&#xff0c;就是使用一段时间内的平均上涨除以平均上涨加平均下跌&#xff08;取正值&#xff09;。也就意味着RSI指数的取值是[0,100]之间&#xff0c;其中0表示周期内没有上涨的&#xff0c;100表示周期内没有下跌的。RSI的直观意义是它表示了一段周期…

用神经网络玩转数据聚类:自编码器的原理与实践

目录 引言一、什么是自编码器二、自编码器的应用场景三、自编码器的优缺点四、如何实现基于自编码器的聚类算法五、总结 引言 随着数据量的爆炸性增长&#xff0c;如何有效地处理和分析数据成为了一个重要的问题。数据聚类是一种常用的数据分析方法&#xff0c;它可以将数据集…

gateway做token校验

本文使用springcloud的gateway做token校验 登录的本质&#xff1a;拿用户名和密码 换 token。 token会返回给浏览器&#xff08;存储&#xff09;&#xff0c;当访问的时候&#xff0c;携带token 发起请求。 token校验图 引入redis依赖 <dependency><groupId>or…

codeforces代:

感受思维的美丽&#xff0c;abcde题目的思路是怎么样的&#xff1a; 上蓝 上紫 可以代 &#xff1a;有问题可以评论区 直接问我 也可以q: 639682754

Django入门

Day1 django环境安装 创建虚拟环境 # step1 创建虚拟环境 python3 -m venv datawhale_django # step2 mac进入虚拟环境 source ./datawhale_django/bin/activate # step3 退出虚拟环境 deactivate安装包 pip3 install django ​pip3 install djangorestframework​​ pip3 …

关于selenium 元素定位的浅度解析

一、By类单一属性定位 元素名称 描述 Webdriver API id id属性 driver.find_element(By.ID, "id属性值") name name属性 driver.find_element(By.NAME, "name属性值") class_name class属性 driver.find_element(By.CLASS_NAME, "class_na…

【Vue-Router】路由入门

路由&#xff08;Routing&#xff09;是指确定网站或应用程序中特定页面的方式。在Web开发中&#xff0c;路由用于根据URL的不同部分来确定应用程序中应该显示哪个内容。 构建前端项目 npm init vuelatest //或者 npm init vitelatest安装依赖和路由 npm install npm instal…

VSCode如何设置高亮

一、概述 本文主要介绍在 VSCode 看代码时&#xff0c;怎样使某个单词高亮显示&#xff0c;主要通过以下三步实现&#xff1a; 安装 highlight-words 插件 配置 highlight-words 插件 设置高亮快捷键F8 工作是嵌入式开发的&#xff0c;代码主要是C/C的&#xff0c;之前一直用…

什么是媒体代发布?媒体代发布注意事项

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体代发布是指将新闻稿或其他宣传内容委托给专业的媒体代理机构或公司进行发布和推广的活动。这些机构通常拥有丰富的媒体资源、人脉和经验&#xff0c;能够更好地将信息传递给目标受众…

数学分析:曲线曲面积分

这一章还是很重要的&#xff0c;可以看到为什么dt1^dt2和dt1dt2是一样的。 可以看到&#xff0c;核心还是黎曼和&#xff0c;我们把两种微分的黎曼和都列出来&#xff0c;并且证明两个相等&#xff0c;即可。 这里要注意&#xff0c;微分形式的积分&#xff0c;在黎曼和的情况…

布置Zabbix监控

一、在 Web 页面中添加 agent 主机 1.1打开Zabbix的Web页面 2.2在 Web 页面中添加 agent 主机 二、在 Web 页面创建自定义监控项模板 2.1创建模版

django实现登录和登录的鉴权

1、创建数据库的管理员表 在models.py 中定义admin表&#xff0c;为了简单&#xff0c;表里只有用户名和密码还有默认加的id 三个字段 from django.db import models# Create your models here.class Admin(models.Model):username models.CharField(verbose_name"用户…

【JavaEE进阶】Spring 更简单的读取和存储对象

文章目录 一. 存储Bean对象1. 配置扫描路径2. 添加注解存储 Bean 对象2.1 使用五大类注解存储Bean2.2 为什么要有五大类注解&#xff1f;2.3 有关获取Bean参数的命名规则 3. 使用方法注解储存 Bean 对象3.1 方法注解储存对象的用法3.2 Bean的重命名3.3 同⼀类型多个 Bean 报错 …

【力扣每日一题】88. 合并两个有序数组 双指针 辅助数组 8.13打卡

文章目录 题目思路代码 题目 88. 合并两个有序数组 难度&#xff1a; 简单 描述&#xff1a; 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&am…

Excel(1):表头或列头冻结

1.需求 对于较大的excel&#xff0c;通常需要固定一部分内容&#xff0c;另一份内容为可翻动。 2.解决方式 在视图中选择冻结窗格&#xff0c;需要注意的是&#xff0c;选择冻结窗格时&#xff0c;窗格的左上方的表格区域是固定不动的&#xff0c;只可以向下或者向右活动。

TCP/IP 下的计算机网络江湖

〇、引言 在当今数字化时代,计算机网络宛如广袤江湖,涵盖着五大门派:物理层、数据链路层、网络层、传输层和应用层。每个门派独具技能,共同构筑着现代网络的框架。物理层宛如江湖基石,将比特流传输;数据链路层如武林传承,组织数据帧传递;网络层则像导航大师,寻找传送路…

EMQX Enterprise 5.1 正式发布:生产环境就绪的 MQTT over QUIC、基于 MQTT 的文件传输支持

近日&#xff0c;企业级 MQTT 物联网接入平台 EMQX Enterprise 5.1 正式发布。该版本为用户提供了更强大、更灵活的物联网解决方案&#xff0c;通过简化功能操作与管理流程&#xff0c;帮助用户快速构建所需的业务。 新版本提供了更大规模且更具伸缩性的全新集群架构&#xff…

Mac M2 Pro安装使用Cocoapods

Mac Pro M2安装使用Cocoapods 在新公司要做iOS开发&#xff0c;所以在新电脑上安装Cocoapods 在升级gem&#xff0c;sudo gem update --system&#xff0c;和安装cocoapods时都遇到如下的提示&#xff1a; ERROR: While executing gem ... (Errno::EPERM)Operation not per…