Locale+Jackson导致Controller接口StackOverflowError异常解决

问题

由于参与的项目有出海需求,即需要给外国人使用,即:需要支持i18n(Internationalization的缩写,共20个字母,除去首尾两个字母,中间有18个,故简称i18n)。

本来是好的,非常简单的Controller接口,在增加字段后,突然爆出StackOverflowError异常:

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowErrorat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1087)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)at javax.servlet.http.HttpServlet.service(HttpServlet.java:555)

排查

直接请教ChatGPT,给出如下几种可能导致Controller层接口StackOverflowError异常的场景:

  • Controller方法导致无限递归:直接或间接调用自己
  • JSON序列化导致无限递归:Controller返回的对象包含双向引用,Jackson序列化时可能会陷入无限递归。
@Data
@Entity
public class User {@Idprivate Long id;private String name;@OneToMany(mappedBy = "user")private List<Order> orders;
}@Data
@Entity
public class Order {@Idprivate Long id;private String product;@ManyToOneprivate User user; // 双向引用
}

当User关联Order,Order也关联User,Jackson解析时会无限嵌套,导致StackOverflowError。

解决方案
使用@JsonManagedReference和@JsonBackReference解决循环引用:

@Data
@Entity
public class User {@Idprivate Long id;private String name;@OneToMany(mappedBy = "user")@JsonManagedReferenceprivate List<Order> orders;
}@Data
@Entity
public class Order {@Idprivate Long id;private String product;@ManyToOne@JsonBackReferenceprivate User user;
}

或使用@JsonIgnore:

@ManyToOne
@JsonIgnore
private User user;
  • AOP拦截器陷入无限调用:错误使用Spring AOP或拦截器,可能会导致方法被无限调用;
  • 错误的toString()方法:实体类toString()方法递归调用自身字段。

四种可能性,第二种可能性最大;于是将问题定位到JSON序列化上。

我的Controller层接口,返回对象(即responseBody)并没有包含双向引用;于是将注意力转移到接口的requestBody上。

定位

经过分析,是我在@RequestBody标注的Dto实体类里新增一个Locale字段:

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@ApiModel(description = "消息创建请求体")
public class MessageCreateDto {private Locale locale;
}

去掉这个字段,就不再有StackOverflowError异常。

方案

定位到问题后,怎么解决问题呢?

还是直接请教ChatGPT,给出的分析:

Locale本身不是一个普通的Java Bean,它没有默认的无参构造方法,并且Jackson可能无法正确解析它,导致JSON反序列化时进入递归调用,最终导致StackOverflowError。

给出的几种解决方案:

方案一:使用@JsonCreator和@JsonValue

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;public class MessageCreateDto {private Locale locale;@JsonCreatorpublic static Locale fromString(String value) {// 解析 "en_US" => Locale("en", "US")return Locale.forLanguageTag(value.replace('_', '-'));}@JsonValuepublic String toJson() {// 让 Jackson 以 "en-US" 格式序列化return locale.toLanguageTag();}
}

经过验证:并没有解决问题。

方案二:自定义Jackson反序列化器

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;public class LocaleDeserializer extends JsonDeserializer<Locale> {@Overridepublic Locale deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {String localeStr = p.getText();return Locale.forLanguageTag(localeStr.replace('_', '-'));}
}

然后在实体类加上注解:

	@JsonDeserialize(using = LocaleDeserializer.class)@JsonSerialize(using = LocaleSerializer.class)private Locale locale;

经过验证:并没有解决问题。

再加上序列化器,还是有StackOverflowError异常。

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;public class LocaleSerializer extends JsonSerializer<Locale> {@Overridepublic void serialize(Locale locale, JsonGenerator gen, SerializerProvider serializers) throws IOException {if (locale != null) {// 格式化为 "en-US" 这种形式,而不是 "en_US"gen.writeString(locale.toLanguageTag());} else {gen.writeNull();}}
}

方案三:手动转换String

public class MessageCreateDto {private String projectId;// 直接使用Locale类型,Jackson反序列化报错StackOverflowErrorprivate String locale;public Locale getLocale() {return Locale.forLanguageTag(locale.replace('_', '-'));}
}

经过验证,此方案可行。

反思

上面的方案三虽然可以解决问题。

但是!!

遗留问题:如果我有5个Java应用,每个应用都有20~30个不等的Controller层接口需要支持i18n,那我得在每个应用,每个Controller层接口里加上String locale字段,然后再增加一个getLocale方法么?

稍微熟悉HTTP,或有一点点前端开发经验,或使用F12快捷键查看过Chrome控制台,就知道有个accept-language HTTPheader:
在这里插入图片描述
因此,不是加字段,而是:

  • 前端在全局配置文件里设置accept-language
  • 所有请求接口里自动带上此header;
  • 后端接口按需解析HTTP header,然后做i18n处理。

题外话

zh_CN和zh-CN

关于Locale,到底是使用下划线(即,_)还是横杠(即,-,专业说法,其实是减号连字符

参考zh-CN还是zh_CN。

另外Java API也该出它的立场,zh_CN是老式写法,zh-CN才是推荐的写法。如下图所示,有一个replace动作:
在这里插入图片描述

Locale.US.toString()和Locale.US.toLanguageTag()

前者是老式写法(中间有空格),后者是新式写法:
在这里插入图片描述

参考

  • ChatGPT

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24769.html

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

相关文章

Graph and GNN——图的表示与图神经网络的介绍与应用

Hi&#xff0c;大家好&#xff0c;我是半亩花海。细数日子已然有很长一段时间没有更新博客了&#xff0c;不是在忙东忙西&#xff0c;就是在玩这玩那&#xff0c;在家摆&#xff0c;在学校gap&#xff0c;无敌了。言归正传&#xff0c;今天暂且先进一步探索并整理一部分图神经网…

京准电钟:NTP精密时钟服务器在自动化系统中的作用

京准电钟&#xff1a;NTP精密时钟服务器在自动化系统中的作用 京准电钟&#xff1a;NTP精密时钟服务器在自动化系统中的作用 NTP精密时钟服务器在自动化系统中的作用非常重要&#xff0c;特别是在需要高精度时间同步的场景中。NTP能够提供毫秒级的时间同步精度&#xff0c;这…

Https解决了Http的哪些问题

部分内容来源&#xff1a;小林coding 详细解析 Http的风险 HTTP 由于是明文传输&#xff0c;所以安全上存在以下三个风险&#xff1a; 1.窃听风险 比如通信链路上可以获取通信内容&#xff0c;用户号容易没。 2.篡改风险 比如强制植入垃圾广告&#xff0c;视觉污染&#…

GO 进行编译时插桩,实现零码注入

Go 编译时插桩 Go 语言的编译时插桩是一种在编译阶段自动注入监控代码的技术&#xff0c;目的是在不修改业务代码的情况下&#xff0c;实现对应用程序的监控和追踪。 基本原理 Go 编译时插桩的核心思想是通过在编译过程中对源代码进行分析和修改&#xff0c;将监控代码注入到…

flex布局自定义一行几栏,靠左对齐===grid布局

模板 <div class"content"><div class"item">1222</div><div class"item">1222</div><div class"item">1222</div><div class"item">1222</div><div class"…

微软推出Office免费版,限制诸多,只能编辑不能保存到本地

易采游戏网2月25日独家消息&#xff1a;微软宣布推出一款免费的Office版本&#xff0c;允许用户进行基础文档编辑操作&#xff0c;但限制颇多&#xff0c;其中最引人关注的是用户无法将文件保存到本地。这一举措引发了广泛讨论&#xff0c;业界人士对其背后的商业策略和用户体验…

NLP的预处理数据

处理文本数据的主要工具是Tokenizer。Tokenizer根据一组规则将文本拆分为tokens。然后将这些tokens转换为数字&#xff0c;然后转换为张量&#xff0c;成为模型的输入。模型所需的任何附加输入都由Tokenizer添加。 如果您计划使用预训练模型&#xff0c;重要的是使用与之关联的…

应用的负载均衡

概述 负载均衡&#xff08;Load Balancing&#xff09; 调度后方的多台机器&#xff0c;以统一的接口对外提供服务&#xff0c;承担此职责的技术组件被称为“负载均衡”。 负载均衡器将传入的请求分发到应用服务器和数据库等计算资源。负载均衡是计算机网络中一种用于优化资源利…

C# 根据Ollama+DeepSeekR1开发本地AI辅助办公助手

在上一篇《访问DeepSeekR1本地部署API服务搭建自己的AI办公助手》中&#xff0c;我们通过通过Ollama提供的本地API接口用Python实现了一个简易的AI办公助手&#xff0c;但是需要运行Py脚本&#xff0c;还比较麻烦&#xff0c;下面我们用C#依据Ollama提供的API接口开发一个本地A…

springboot+dubbo+zookeeper的注册服务和调用实践

目录 zookeeper为什么可作为注册中心zookeeper注册中心优缺点启动zookeeper编写springboot项目提供dubbo服务1. 服务接口2. Springboot引入dubbo实现服务接口2.1 工程目录和依赖2.2 启动程序和application.properties2.3 DubboService 实现服务接口2.4 测试api&#xff0c;用于…

NL2SQL的应用-长上下文模型在处理NL2SQL任务时,相较于传统模型,有哪些显著的优势

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下NL2SQL的应用-长上下文模型在处理NL2SQL任务时&#xff0c;相较于传统模型&#xff0c;有哪些显著的优势。NL2SQL&#xff08;自然语言转SQL&#xff09;技术旨在将用户自然语言提问自动转换为结构化查询语句&#…

A Large Recurrent Action Model: xLSTM Enables Fast Inference for Robotics Tasks

奥地利林茨约翰开普勒大学机器学习研究所 ELLIS 小组&#xff0c;LIT 人工智能实验室奥地利林茨 NXAI 有限公司谷歌 DeepMind米拉 - 魁北克人工智能研究所 摘要 近年来&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&#xff09;领域出现了一种趋势&#xff0c;…

DeepSeek本地部署+自主开发对话Web应用

文章目录 引言前端部分核心页面DeepSeek.vueMyModal.vue 后端部分WebSocketConfig 配置类AbstractDeepSeekToolDeepSeekWebSocketHandler 数据库设计总结 引言 最近DeepSeep横空出世&#xff0c;在全球内掀起一股热潮&#xff0c;到处都是满血大模型接入的应用&#xff0c;但这…

DMA 定制固件教程:小白跟做即得单人固件,超详细纯喂饭教程,100% 成功秘籍!FPGA仿真1:1、中断逻辑和TLP核心都在。

DMA 定制固件教程 小白跟着操作做可以做出的单人固件 图文教程 链接&#xff1a;https://docs.qq.com/doc/DQ01lVGtHelROVHNv 本图文教程包含内容&#xff1a; 一、DMA仿真技术采集真实单人固件 二、网卡TLP仿真固件生成 三、DMA仿真技术io、中断逻辑&#xff0c;从零仿真 四、…

Linux | Ubuntu 与 Windows 双系统安装 / 高频故障 / UEFI 安全引导禁用

注&#xff1a;本文为 “buntu 与 Windows 双系统及高频故障解决” 相关文章合辑。 英文引文&#xff0c;机翻未校。 How to install Ubuntu 20.04 and dual boot alongside Windows 10 如何将 Ubuntu 20.04 和双启动与 Windows 10 一起安装 Dave’s RoboShack Published in…

spring中的注解介绍

本篇文章专门用来介绍spring中的各种注解。 1、RestController 1、含义 2、举例 3、使用场景 RestController 通常用于开发 RESTful API&#xff0c;适合返回 JSON 或 XML 数据的场景 4、总结 RestController 是 Spring 中用于简化 RESTful Web 服务开发的注解&#xff0c;它结…

JVM生产环境问题定位与解决实战(二):JConsole、VisualVM到MAT的高级应用

生产问题定位指南&#xff1a;几款必备的可视化工具 引言 在上一篇文章中&#xff0c;详细的介绍了JDK自带的一系列命令行工具&#xff0c;&#xff0c;如jps、jmap、jstat、jstack以及jcmd等&#xff0c;这些工具为排查和诊断Java虚拟机&#xff08;JVM&#xff09;问题提供…

网页制作09-html,css,javascript初认识のhtml如何使用表单

表单主要用来收集客户端提供的相关信息。,使网页具有交互作用。在网页制作的过程中&#xff0c;常常需要使用表单&#xff0c;如进行会员注册&#xff0c;网上调查和搜索等 访问者可以使用如文本域列表框&#xff0c;复选框以及单选按钮之类的表单对象输入信息&#xff0c;然后…

基本网络安全的实现

基本网络安全的实现 一 &#xff1a;AAA AAA 是Authentication&#xff0c;Authorization and Accounting&#xff08;认证、授权和计费&#xff09;的简 称&#xff0c;它提供了一个用来对认证、授权和计费这三种安全功能进行配置的一致性框架&#xff0c; 它是对网络安全…

Jupyter Notebook~Anaconda3安装教程

一、下载anaconda&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 百度网盘通道&#xff0c;链接:https://pan.baidu.com/s/1gyVDG2p71neFXi8VwXgvEQ?pwdewn7提取码: ewn7 二、安装 1、右击安装软件选择【以管理员身份运行】&#xff0c;点击【Next】…