API 传输数据 小心 fastjson 循环引用检测($ref)会作怪
API 传输数据时 fastjson 循环引用检测$ref会作怪一、背景在HistoryPriceHandler中需要根据一批crmId构造出多个HistoryPriceQuery再调用wso2Utils.getHistoryPrice(...)发送给 WSO2 历史价格接口。构造逻辑大致是ListHistoryPriceQuerypriceQueryHistoryPriceQuery.build(crmIdList,query);ListApimHistoryPriceResponsepriceListwso2Utils.getHistoryPrice(priceQuery);每个HistoryPriceQuery内部都持有一份mtmListCpmMtm。二、优化引发的问题出于「节约内存」的想法我把build改成了共享同一份 mtm 列表所有HistoryPriceQuery的mtm字段都指向同一个 List 对象因为每个 query 的 mtm 内容本来就完全一样。// 共享版mtm 只克隆一次所有 query 共用同一个引用ListCpmMtmsharedMtmListbuildMtmList(query.getMtm());for(...){metaQuery.setMtm(sharedMtmList);// 同一个对象}结果出现了诡异现象原来的独立克隆版本getHistoryPrice调用成功。共享 mtm 的新版本getHistoryPrice调用失败。而更迷惑的是——两个版本用System.out.println(JSON.toJSON(...))打印出来的 JSON看起来一模一样。三、根因fastjson 的循环引用检测$ref3.1 什么是 $reffastjson 在序列化时会记录已经序列化过的对象。如果同一个对象引用在结构里出现第二次它不会再展开而是写成一个引用标记{$ref:$[0].mtm}$ref路径语法$根对象$[0]根数组第 0 个元素$[0].mtm第 0 个元素的 mtm 字段它的本意是防止循环引用导致的无限递归StackOverflowError例如父子对象互相持有的场景。3.2 判定依据是「引用相同」而非「内容相同」关键点fastjson 用的是**对象身份同一个内存地址**来判重类似IdentityHashMap不是用equals()比内容。情况是否同一个引用会不会 $ref同一个 List 被多个字段引用共享 mtm是成立会两个不同对象内容一模一样独立克隆否不成立不会推论同一个对象引用 → 内容一定相同成立因为就是同一个对象。内容相同 → 是同一个对象不成立可能是内容巧合相同的独立对象。所以「内容相同但不是同一个实例」不会触发$ref不会出问题。3.3 为什么这次会踩坑共享 mtm 后多个HistoryPriceQuery的mtm指向同一个 List 实例。fastjson 序列化时[{key:0,mtm:[{material:82H803GJJP,key:0}]},// 第一次完整展开{key:1,mtm:{$ref:$[0].mtm}},// 第二次变成 $ref{key:2,mtm:{$ref:$[0].mtm}}// 第三次变成 $ref]WSO2 接口不认识$ref它是 fastjson 私有语法不是标准 JSON 语义解析畸形报文失败 → 请求报错。独立克隆版本里每个 mtm 都是不同对象没有重复引用不会出现$ref报文干净 → 成功。四、为什么「打印一样、发送却失败」这是最迷惑的一点原因在于两个 API 的差异JSON.toJSON(obj)先把对象转成一棵全新的 JSONObject/JSONArray 树转换过程把共享引用摊平成各自独立的节点再toString。此时已经没有「同一对象」了所以打印结果正常、看不出$ref。JSON.toJSONString(obj)直接序列化原始对象共享引用仍然存在 → 触发$ref。getHistoryPrice内部发请求时用的是toJSONString或等价的直接序列化所以才会带$ref。结论判断有没有$ref一定要看toJSONString的结果不能用toJSON。五、如何检测是否触发了 $ref方法 1直接打印 toJSONStringSystem.out.println(JSON.toJSONString(priceQuery));输出里出现{$ref:...}即触发。方法 2字符串搜 $refStringjsonJSON.toJSONString(priceQuery);booleanhasRefjson.contains($ref);System.out.println(包含 $ref hasRef);方法 3对比开/关检测的结果importcom.alibaba.fastjson.serializer.SerializerFeature;StringwithDetectJSON.toJSONString(priceQuery);// 默认开检测可能有 $refStringwithoutDetectJSON.toJSONString(priceQuery,SerializerFeature.DisableCircularReferenceDetect);// 关检测全展开System.out.println(两者是否不同 !withDetect.equals(withoutDetect));两者不同即说明默认序列化里存在$ref。六、解决方案方案说明建议独立克隆不共享引用每个对象各自持有独立实例从源头避免重复引用✅ 推荐关闭引用检测JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect)需改到每个序列化点易漏⚠️ 谨慎全局关闭影响面大❌ 不建议本次采用独立克隆虽然多占一点内存但对外传输数据正确、稳定不用碰序列化配置是最省心的选择。七、经验总结对外传输尤其发给第三方接口的对象避免多个字段/元素共享同一个可序列化对象实例否则 fastjson 会用$ref表示重复引用导致对端解析失败。$ref只认同一实例不认equals内容相同。内容相同但不同实例是安全的。排查时用toJSONString看真实报文别用toJSON后者会把引用摊平掩盖问题。「省内存的共享」和「对外序列化的安全」有时是冲突的涉及序列化传输的场景优先保证隔离性独立实例。

相关新闻

迪奥720丝绒红棕,源头工厂敢接单的底线在哪?代工老板别被“大牌同源”忽悠了

迪奥720丝绒红棕,源头工厂敢接单的底线在哪?代工老板别被“大牌同源”忽悠了

最近好几个做私域团长的老朋友找我,开口就问:“能不能搞到跟某法系头部品牌720同款质感的红棕调口红?客户就要那个丝绒哑光、高折射率光泽的上唇体验。”我直接泼冷水:15元一支,连正经的合成酯和高折射率油脂都买不到&…

2026/10/20 15:09:41 阅读更多 →
开年第一个月,好几个做私域团长的老朋友打来电话,开口就问:迪奥720那款磁吸翻盖口红的包材,到底能不能做到“大牌同源”级别的公差?我直接回他们:别跟我聊“复刻”,咱们就聊车间里真金白银的包材定制和进货

开年第一个月,好几个做私域团长的老朋友打来电话,开口就问:迪奥720那款磁吸翻盖口红的包材,到底能不能做到“大牌同源”级别的公差?我直接回他们:别跟我聊“复刻”,咱们就聊车间里真金白银的包材定制和进货

做D家720同款磁吸翻盖口红,包材结构看似简单,但产线一开,全是工业细节。磁吸闭合是第一道生死关——普通铁氧体磁铁吸力仅约0.2Ncm,开合数次即松动;而正规代工厂必须采用钕磁铁,吸力稳定≥0.3Ncm&#xff0…

2026/10/17 19:32:32 阅读更多 →
Unity 程序员编曲第二天:当经典片头遇上失真吉他

Unity 程序员编曲第二天:当经典片头遇上失真吉他

Unity 程序员学编曲的第二天。上一期,我挑战了那首大家耳熟能详的《新闻联播》片头曲;这一期,继续在它的基础上进行改编,尝试把它变成一个摇滚风格的版本。熟悉的旋律配上更有力量的节奏,竟然也碰撞出了别样的味道。 U…

2026/10/18 15:06:40 阅读更多 →

最新新闻

VS Code Jupyter内核崩溃排查:Kernel died with exit code 1全链路解析

VS Code Jupyter内核崩溃排查:Kernel died with exit code 1全链路解析

1. 从一个让人抓狂的报错说起如果你在 VS Code 里用 Jupyter 跑过代码,大概率见过这个弹窗:"Kernel died with exit code 1"。它不像语法错误那样给你一行红色波浪线,也不像缺包那样告诉你ModuleNotFoundError,它就是一…

2026/10/20 23:43:35 阅读更多 →
5秒语音克隆:Chatterbox-Turbo如何实现48kHz高保真音色复制

5秒语音克隆:Chatterbox-Turbo如何实现48kHz高保真音色复制

1. 五秒语音,听起来像什么:这套方案到底做了什么1.1 从“录三小时”到“录一句话”的跨越语音克隆这件事,早几年还是配音圈和极客圈的玩具。传统方案想克隆一个声音,至少要准备几十条、最优情况下一两个小时的高质量干音&#xff…

2026/10/20 23:43:35 阅读更多 →
C++ OpenCV图像处理流水线:从平滑到面积测量的完整实现

C++ OpenCV图像处理流水线:从平滑到面积测量的完整实现

简介:面向图像处理初学者的C实验项目,源自中国科技大学图像测量课程,围绕图像平滑、增强、边缘检测、阈值化、细化、面积测量及区域边界提取与周长计算展开,适合课程实验复现、毕业设计参考或算法入门。代码按实验划分成独立目录&…

2026/10/20 23:43:35 阅读更多 →
论文查重与AI痕迹检测:底层逻辑、降重实操与合规工具使用

论文查重与AI痕迹检测:底层逻辑、降重实操与合规工具使用

论文重复率和AI痕迹这两道关卡,确实是眼下不少同学毕业季最头疼的事。尤其是当学校同时接入两套检测系统,这边降下去了、那边又标红,再叠加“AI痕迹”这个新指标,很多人的论文改到崩溃也没搞清楚问题到底出在哪。我折腾过不少类似…

2026/10/20 23:43:35 阅读更多 →
论文查重与AI检测双重压力下的降重实战指南

论文查重与AI检测双重压力下的降重实战指南

每年到毕业季,总有同学拿着查重报告来找我,第一句话基本都是“师兄,这个降重到底怎么降”。我见过最夸张的一份初稿,查重率99.8%,基本整篇标红,学生说用了某个“一键降重工具”后反而越改越乱,最…

2026/10/20 23:43:35 阅读更多 →
Linux挂载NTFS报独占错误?排查链路与解决方案全解析

Linux挂载NTFS报独占错误?排查链路与解决方案全解析

1. 这个报错到底在说什么如果你在 Linux 或 macOS 上挂载一块 Windows 格式(NTFS)的移动硬盘,突然蹦出这么一行字:Mount is denied because the NTFS volume is already exclusively opened. The volume may be already mounted, …

2026/10/20 23:42:34 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →