使用基于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.hqwc.cn/news/67445.html

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

相关文章

企业直播MR虚拟直播(MR混合现实直播技术)视频介绍

到底什么是企业直播MR虚拟直播&#xff08;MR混合现实直播技术&#xff09;&#xff1f; 企业直播MR虚拟直播新玩法&#xff08;MR混合现实直播技术&#xff09; 我的文章推荐&#xff1a; [视频图文] 线上研讨会是什么&#xff0c;企业对内对外培训可以用线上研讨会吗&#x…

Python学习笔记_基础篇(三)_数据类型之列表

一.基本数据类型 整数&#xff1a;int 字符串&#xff1a;str(注&#xff1a;\t等于一个tab键) 布尔值&#xff1a; bool 列表&#xff1a;list &#xff08;元素的集合&#xff09; 列表用[] 元祖&#xff1a;tuple 元祖用&#xff08;&#xff09; 字典&#xff1a;dict 注&a…

【量化课程】08_2.深度学习量化策略基础实战

文章目录 1. 深度学习简介2. 常用深度学习模型架构2.1 LSTM 介绍2.2 LSTM在股票预测中的应用 3. 模块分类3.1 卷积层3.2 池化层3.3 全连接层3.4 Dropout层 4. 深度学习模型构建5. 策略实现 1. 深度学习简介 深度学习是模拟人脑进行分析学习的神经网络。 2. 常用深度学习模型架…

Python语言入门简介

一、Python的历史 关键点: Python是一种用途广泛、解释性、面向对象的程序设计语言。 Python是新西兰的Guido van Rossum在1990 年创建的&#xff0c;它以英国流行喜剧“Monty Python的飞行马戏团”命名。van Rossum将Python开发作为一个嗜好&#xff0c;Python 因其简单、简洁…

flutter开发实战-实现左右来回移动的按钮引导动画效果

flutter开发实战-实现左右来回移动的按钮引导动画效果 最近开发过程中需要实现左右来回移动的按钮引导动画效果 一、动画 AnimationController用来控制一个或者多个动画的正向、反向、停止等相关动画操作。在默认情况下AnimationController是按照线性进行动画播放的。Animati…

VBA技术资料MF42:VBA_从Excel中上面的单元格复制公式

【分享成果&#xff0c;随喜正能量】唯有梦想才配让你不安&#xff0c;唯有行动才能解除你的不安.绳锯木断&#xff0c;水滴石穿。也许你现在做的事情很小&#xff0c;只要你能日积月累的坚持下去&#xff0c;才会发现意义非凡。所谓的成功&#xff0c;便是别人失败的时候你还在…

每天一道leetcode:剑指 Offer 13. 机器人的运动范围(中等广度优先遍历剪枝)

今日份题目&#xff1a; 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0]的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&#xff0c;也不能进入行坐标和列坐标的数位之…

ArcGIS入门操作手册

一.ArcGIS安装过程 参考本人博客&#xff1a;保姆级Arcgis安装图文安装教程_追忆苔上雪的博客-CSDN博客 二.ArcGIS植被指数计算 (1)使用工具&#xff1a;栅格计算器 打开软件&#xff0c;右侧搜索栅格计算器打开&#xff0c;要是搜索栏不小心叉掉找不到了&#xff0c;可以通…

从零开始 Spring Cloud 10:Elasticsearch

从零开始 Spring Cloud 10&#xff1a;Elasticsearch 图源&#xff1a;laiketui.com 基础 什么是 Elasticsearch Elasticsearch 是一个开源搜索引擎&#xff0c;可以用它实现从海量数据中对数据的高效查询。 关于 Elasticsearch 的历史渊源以及广泛用途&#xff0c;可以观看…

地理数据的双重呈现:GIS与数据可视化

前一篇文章带大家了解了GIS与三维GIS的关系&#xff0c;本文就GIS话题带大家一起探讨一下GIS和数据可视化之间的关系。 GIS&#xff08;地理信息系统&#xff09;和数据可视化在地理信息科学领域扮演着重要的角色&#xff0c;它们之间密切相关且相互增强。GIS是一种用于采集、…

Spark_Spark中 Stage, Job 划分依据 , Job, Stage, Task 高阶知识

上一篇文章中 &#xff1a; Spark_Spark 中 Stage, Job 划分依据 , Job, Stage, Task 基础知识_spark stage job_高达一号的博客-CSDN博客 主要解读了Stage, job, task 的划分标准&#xff0c;这篇文章将对这些信息进行进一步解读。 一. Job、Stage、Task的概念 在讲Spark的任…

【图像分类】理论篇 (4)图像增强opencv实现

随机旋转 随机旋转是一种图像增强技术&#xff0c;它通过将图像以随机角度进行旋转来增加数据的多样性&#xff0c;从而帮助改善模型的鲁棒性和泛化能力。这在训练深度学习模型时尤其有用&#xff0c;可以使模型更好地适应各种角度的输入。 原图像&#xff1a; 旋转后的图像&…