使用基于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.pswp.cn/news/35791.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35791.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35791.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【JVM】CPU飆高排查方案與思路

文章目錄 CPU飆高排查方案與思路 CPU飆高排查方案與思路 1.使用top命令查看占用cpu的情況 2.通過top命令查看后&#xff0c;可以查看是哪一個進程占用cpu較高&#xff0c;上圖所示的進程為&#xff1a;40940 3.查看進程中的線程信息 4.可以根據進程 id 找到有問題的線程&a…

掛載 IK 分詞器至 Elasticsearch Docker 容器 - Docker Docker Compose 教程

簡介 本博客將講解如何在 Docker 和 Docker-Compose 中運行 Elasticsearch&#xff0c;并掛載 IK 分詞器。 步驟 一、快速運行Elasticsearch:8.1.3 1.首先&#xff0c;我們需要創建一個新的 Docker 網絡&#xff1a;"elastic"。這個網絡會提供給我們接下來所要創…

Ceph分布式存儲系統優化分析

Ceph支持多種存儲訪問接口&#xff0c;現有的多種性能測試工具都可用于Ceph的性能測試&#xff0c;如測試塊接口性能的fio&#xff0c;iometer等&#xff1b;測試CephFS接口的filebench&#xff0c;fio等;測試對象接口的cosbench等。Ceph有專用的基準測試集CBT&#xff0c;其包…

1. VisionOS平臺介紹

介紹 VisionOS 可實現與現實世界無縫集成并與其他虛擬內容共存的 3D 多任務體驗。這為個人生產力、生活方式和娛樂應用打開了一個充滿新可能性的世界&#xff0c;并為開發人員打開了一個全新的市場。然而&#xff0c;它也帶來了圍繞多任務處理和與身體互動的新挑戰。Unity Poly…

【數據結構與算法】十大經典排序算法-插入排序

&#x1f31f;個人博客&#xff1a;www.hellocode.top &#x1f3f0;Java知識導航&#xff1a;Java-Navigate &#x1f525;CSDN&#xff1a;HelloCode. &#x1f31e;知乎&#xff1a;HelloCode &#x1f334;掘金&#xff1a;HelloCode ?如有問題&#xff0c;歡迎指正&#…

云原生應用場景及交付部署

云原生是一種軟件架構和開發方式&#xff0c;旨在支持在云環境中構建、部署和管理應用程序。它是為了克服傳統應用程序在云環境中所面臨的挑戰而提出的一種方法。云原生應用場景廣泛&#xff0c;以下是一些常見的云原生應用場景&#xff0c;并提供了一些詳細解釋&#xff1a; …

第57步 深度學習圖像識別:CNN可視化(Pytorch)

基于WIN10的64位系統演示 一、寫在前面 由于不少模型使用的是Pytorch&#xff0c;因此這一期補上基于Pytorch實現CNN可視化的教程和代碼&#xff0c;以SqueezeNet模型為例。 二、CNN可視化實戰 繼續使用胸片的數據集&#xff1a;肺結核病人和健康人的胸片的識別。其中&…

問DAO成都丨CyberDAO共識會議在成都圓滿落幕

過往匆匆&#xff0c;唯有共識綿延&#xff1b;未來已來&#xff0c;愿與智者同謀。2023年8月9日至8月10日&#xff0c;CyberDAO共識會議在成都市大邑縣順利召開&#xff0c;吸引了上百名Web3.0與元宇宙愛好者參與本次會議。CyberDAO大中華區運營團隊合伙人JR、漫威、安祈、可樂…

【0.1】lubancat魯班貓4刷入debian網絡ping 域名不通問題

目錄 1. 環境2. 操作步驟 1. 環境 lubancat4魯班貓4 (4G0)不帶emmc系統鏡像lubancat-rk3588-debian11-gnome-20230807_update.img官方資料地址https://doc.embedfire.com/products/link/zh/latest/linux/ebf_lubancat.html 2. 操作步驟 從官網給的百度網盤下載linux系統全部…

10、雜項:遍歷指定目錄計算文件的md5并輸出到文件

目錄 &#x1f345;點擊這里查看所有博文 隨著自己工作的進行&#xff0c;接觸到的技術棧也越來越多。給我一個很直觀的感受就是&#xff0c;某一項技術/經驗在剛開始接觸的時候都記得很清楚。往往過了幾個月都會忘記的差不多了&#xff0c;只有經常會用到的東西才有可能真正記…

【Rust】Rust學習 第十一章編寫自動化測試

Rust 是一個相當注重正確性的編程語言&#xff0c;不過正確性是一個難以證明的復雜主題。Rust 的類型系統在此問題上下了很大的功夫&#xff0c;不過它不可能捕獲所有種類的錯誤。為此&#xff0c;Rust 也在語言本身包含了編寫軟件測試的支持。 編寫一個叫做 add_two 的將傳遞…

[C++ 網絡協議編程] TCP/IP協議

目錄 1. TCP/IP協議棧 2. TCP原理 2.1 TCP套接字中的I/O緩沖 2.2 TCP工作原理 2.2.1 三次握手&#xff08;連接&#xff09; 2.2.2 與對方主機的數據交換 2.2.3 四次握手&#xff08;斷開與套接字的連接&#xff09; TCP&#xff08;Transmission Control Protocol傳輸控…

無涯教程-Perl - ref函數

描述 如果EXPR為引用,則此函數返回真值&#xff1b;如果未提供EXPR,則為$_。返回的實際值還定義了引用所引用的實體的類型。 內置類型為- REFSCALARARRAYHASHCODEGLOBLVALUEIO::Handle 如果使用bless()函數為變量設置了祝福,則將返回新的數據類型。新的數據類型通常將是一個…

比較編程語言C和Go

使用一個簡單的計數程序來比較古老的C語言和現代的Go語言。Go是一種現代的編程語言&#xff0c;它在很大程度上源自C語言。因此&#xff0c;對于任何使用C語言編寫程序的人來說&#xff0c;Go可能會感覺很熟悉。Go使得編寫新程序變得容易&#xff0c;同時又讓C程序員感到熟悉&a…

大數據-玩轉數據-Flink 自定義Sink(Mysql)

一、說明 如果Flink沒有提供給我們可以直接使用的連接器&#xff0c;那我們如果想將數據存儲到我們自己的存儲設備中&#xff0c;mysql 的安裝使用請參考 mysql-玩轉數據-centos7下mysql的安裝 創建表 CREATE TABLE sensor (id int(10) ) ENGINEInnoDB DEFAULT CHARSETutf8二…

二 根據用戶行為數據創建ALS模型并召回商品

二 根據用戶行為數據創建ALS模型并召回商品 2.0 用戶行為數據拆分 方便練習可以對數據做拆分處理 pandas的數據分批讀取 chunk 厚厚的一塊 相當大的數量或部分 import pandas as pd reader pd.read_csv(behavior_log.csv,chunksize100,iteratorTrue) count 0; for chunk in …

DNS協議及其工作原理

DNS是域名系統&#xff08;Domain Name System&#xff09;的縮寫&#xff0c;它是一種用于將域名轉換為IP地址的分布式數據庫系統。它是因特網的基石&#xff0c;能夠使人們通過域名方便地訪問互聯網&#xff0c;而無需記住復雜的IP地址。 DNS的歷史可以追溯到1983年&#xf…

4個簡化IT服務臺任務的ChatGPT功能

最近幾個月&#xff0c;ChatGPT 風靡全球&#xff0c;這是一個 AI 聊天機器人&#xff0c;使用戶能夠生成腳本、文章、鍛煉圖表等。這項技術在各行各業都有無窮無盡的應用&#xff0c;在本文中&#xff0c;我們將研究這種現代技術如何幫助服務臺團隊增強服務交付和客戶體驗。 什…

最佳實踐:如何優雅地提交一個 Amazon EMR Serverless 作業?

《大數據平臺架構與原型實現&#xff1a;數據中臺建設實戰》一書由博主歷時三年精心創作&#xff0c;現已通過知名IT圖書品牌電子工業出版社博文視點出版發行&#xff0c;點擊《重磅推薦&#xff1a;建大數據平臺太難了&#xff01;給我發個工程原型吧&#xff01;》了解圖書詳…

章節7:XSS檢測和利用

章節7&#xff1a;XSS檢測和利用 測試payload <script>alert(XSS)</script> <script>alert(document.cookie)</script> ><script>alert(document.cookie)</script> ><script>alert(document.cookie)</script> &qu…