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

这篇具有很好参考价值的文章主要介绍了使用基于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。差异由此出现。

使用基于jvm-sandbox的对三层嵌套类型的改造,后端
image-20230810214520907
使用基于jvm-sandbox的对三层嵌套类型的改造,后端
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 多平台发布文章来源地址https://www.toymoban.com/news/detail-639364.html

到了这里,关于使用基于jvm-sandbox的对三层嵌套类型的改造的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于Proxifier和V2/Ray的嵌套代理方案

    最近遇到了一个比较奇怪的需求:我的电脑只能访问到局域网内的服务器A,而服务器A可以访问到服务器B国内网络, 服务器B可以访问到国外网络。 当电脑简单地使用服务器A进行代理的时候,就可以变成正常的家庭网络了(只能访问国内网络); 但我此时需要使用服务器B进

    2024年03月09日
    浏览(49)
  • 【网络基础实战之路】基于三层架构实现一个企业内网搭建的实战详解

    【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 【网络基础实战之路】基于三个分公司的内网搭建并连

    2024年02月11日
    浏览(38)
  • Mysql中的对数据库字段的数据进行加密

    注意点:此处只对name字段进行了字段类型的修改。 ENCODE(\\\'123\\\',\\\'123\\\'): 第一个123:表示插入数据库中的数据(即将要被转换的数据) 第二个123:表示转换秘钥,可以任意起名。在解密的时候会需要用到。 结果: 注意点1 :此处DECODE的两个参数分别为 需要解密的内容(数据库中

    2024年02月09日
    浏览(69)
  • 计算机网络实验_三层架构企业网络_基于Cisco Packet Tracer模拟器

    1、了解一般企业网络的三层架构模型; 2、了解三层架构企业网络内部的通信流程; 3、理解双核心路由的热备份和负载均衡。 1、背景知识 1.1 分层网络设计概述 在进行组网设计时,一般采用分层组网设计思想,即一个大规模的网络系统往往被分为几个较小的部分,它们之间

    2024年02月12日
    浏览(55)
  • 记一次线上问题引发的对 Mysql 锁机制分析

    最近双十一开门红期间组内出现了一次因 Mysql 死锁导致的线上问题,当时从监控可以看到数据库活跃连接数飙升,导致应用层数据库连接池被打满,后续所有请求都因获取不到连接而失败 整体业务代码精简逻辑如下: 数据库实例监控: 当时通过分析上游问题流量限流解决后

    2024年02月05日
    浏览(56)
  • <Spring Boot>开发基于三层架构设计:Dao层、Service层、Controller层及案例一

    三层架构设计:基于Spring Boot开发要使用三层架构: 数据访问层(Dao)、业务逻辑层(Service)、控制层(Control-ler) (1)数据访问层(Dao):Dao层是最底层的设计,用户操作数据库。通过MyBatis持久化实现接口开发,XML文件。Dao层的设计步骤:1、在数据库中生成数据库表 2通

    2024年02月15日
    浏览(44)
  • 三层交换机配置VLAN和使用OSPF协议

    1.拓扑图: 2.实验要求: (31).IP地址基于192.168.1.0/24 (2).使用OSPF (3).全网可达 3.实验思路: (1)在二层交换机配置创建VLAN,将接口划入VLAN (2)SW3,SW4上接口创建Trunk干道 (3)在SW1,SW2使用ospf实现连接 4.配置命令: 配置PC1 IP(以PC1为例)   SW3,SW4,创建vlan,并将接口划入VLA

    2024年02月16日
    浏览(42)
  • C++类模板——嵌套使用

    目录 类模板的嵌套分类 一、数组的成员是栈 (一)Vector大小为2,Stack大小为3;不对Vector进行扩展;(浅拷贝),只对C++内置数据类型进行拷贝 1)代码 2)注意事项 3)结果 (二)Vector大小为2,Stack大小为3;对Vector进行扩展;(深拷贝),对类的拷贝且类用到了堆区需要重

    2024年02月05日
    浏览(26)
  • 图像处理之《寻找和隐藏:通过深度强化学习的对抗隐写术》论文阅读

    一、文章摘要 图像隐写术的目的是将一个完整大小的图像(称为秘密)隐藏到另一个图像(称为封面)中。以往的图像隐写算法只能在一个封面中隐藏一个秘密。在这篇论文中, 我们提出了一个自适应局部图像隐写(AdaSteg)系统,允许缩放和位置自适应图像隐写 。该系统通过在局部

    2024年03月14日
    浏览(54)
  • mybatis使用collection嵌套查询

        在开发中,可能会遇到一对多的关系,这个时候,一条sql语句就难以胜任这个任务了。只能先执行一条sql,然后根据返回的结果,再做一次sql关联查询,这个时候,使用mybatis的collection就可以实现。     如果第一次查询返回的是一个list集合,那么,后续的查询就是一个

    2023年04月12日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包