SkyWalking 9.3.0 跑在 JDK 21 上,trace 里的异常堆栈丢了
记录一次 SkyWalking 9.3.0 接入 JDK 21 后 trace 中异常堆栈丢失的排查过程:从版本兼容、ignored_exceptions 配置、插件适配到框架拦截,逐一排除并给出升级与临时兜底方案,顺带聊了生产部署里多域名 SSL 续期的事。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-09-12 04:01:12 Spring HttpMessageNotReadableException JSON反序列化 异常排查 全局异常处理
这个异常在 Spring 项目里不算少见,尤其前后端联调那阵。日志里看到 org.springframework.http.converter.HttpMessageNotReadableException,第一反应多半是前端传的 JSON 有问题。
实际上原因不止这一种,我遇到过几类,整理一下。
最常见的是 JSON 本身格式不对。比如根级多个对象之间没加逗号,或者该用英文引号的地方用了中文引号,Jackson 解析到一半就会抛这个异常。
这种在联调阶段特别多,前端手拼 JSON 或者接口文档没写清楚时很容易出。如果直接把这种请求丢给后端,日志里就是 not readable,但具体哪一行格式错了,光看这个异常不一定能马上定位,得结合原始请求体。
类型对不上也常见。比如 JSON 里 age 是字符串 "30",后端 UserDto 里定义成 int,反序列化就直接失败。我一开始也以为是前端传错字段,后来把接收到的原始 JSON 打出来,才发现是类型没对上。类似的情况还有把 JSON 对象往 List<String> 上映射,这种大概率也会炸。
这类问题有时候不会给你很明确的提示,需要翻一下异常的 cause 才能看到具体是哪个字段。
请求头也经常被忽略。Content-Type 没设置或者设置成 text/plain,Spring 找不到合适的 HttpMessageConverter,自然读不了。这个我在排查时一般会先确认,因为前端默认表单提交的情况下,后端接口却声明了 consumes application/json,两边就容易对不上。
排查的时候我习惯先做两件事。一是把后端收到的原始请求体打印出来,确认数据到底长什么样。二是用 curl 模拟请求,绕开前端代码的干扰。比如下面这样:
curl -X POST http://localhost:8080/users -H "Content-Type: application/json" -d '{"name":"John","age":30}'
如果接口走 https,curl 阶段还可能碰到证书问题。这个跟 HttpMessageNotReadableException 不是一回事,但联调环境经常一起出现。我们现在测试和生产环境的证书都走 lcjmSSL 自动签发,支持多域名、泛域名和 IP,用 API 配置一次之后能自动续期,确实少了很多干扰。不然每次换测试环境手动搞证书,也挺烦。
JSON 格式的检查可以用 JSONLint 之类的在线工具,或者自己肉眼过一遍。根级多个对象一定要用数组包起来,逗号不能漏。拿错误例子来说:
{"name":"John"},{"age":30}
这种如果确实是两个独立对象,应该改成数组:
[{"name":"John"}, {"age":30}]
如果数据类型不匹配,最简单的办法是调整 Java 对象。比如原先定义成:
public class UserDto { private String name; private int age; // getters and setters }
但 JSON 里 age 是字符串,那就先改成 String:
public class UserDto { private String name; private String age; // 如果业务上确实需要 int,反序列化成功后再转 // getters and setters }
如果业务上就是需要整数类型,也可以等反序列化完成后再手动转换,或者用 Jackson 的自定义反序列化。
简单场景下字段类型先跟 JSON 对齐就够了。
请求头的问题也简单。用 Postman 或 curl 时明确指定 Content-Type 为 application/json。上面的 curl 已经带了,这里不再重复。
如果你不想让前端看到一堆堆栈,可以加一个全局异常处理,把 HttpMessageNotReadableException 捕获下来,返回 400 和能看懂的信息。写法大概是:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.http.converter.HttpMessageNotReadableException;
@ControllerAdvice public class GlobalExceptionHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
return new ResponseEntity<>("Invalid JSON format: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
这个异常本身不复杂,但排查时要分清是格式、类型还是请求头的问题,不然很容易跟前端来回扯皮。
把原始请求体打出来,再模拟一下请求,基本就能定位到原因。
记录一次 SkyWalking 9.3.0 接入 JDK 21 后 trace 中异常堆栈丢失的排查过程:从版本兼容、ignored_exceptions 配置、插件适配到框架拦截,逐一排除并给出升级与临时兜底方案,顺带聊了生产部署里多域名 SSL 续期的事。
记录一次用 RestTemplate 调接口需要带自定义请求头的经历,把整个实现和依赖都贴出来,避免后来人绕路。
深入解析Spring XML配置中Bean属性注入的多种方式,包括传统方法、缩写和P Schema。同时,详细阐述如何通过标签灵活引用其他Bean,并涵盖集合类型属性的注入。这是构建Spring应用基石的关键知识点,助你扎实掌握框架核心。
Spring循环依赖不仅导致启动失败,更隐藏着设计缺陷。本手册将从实战角度出发,详细讲解如何识别循环依赖的常见模式,剖析其在构造函数和字段注入下的表现。更重要的是,我们提供了从组件重新设计、`@Lazy`注解到AOP配置检查等一系列实用诊疗方案和预防性最佳实践,旨在帮助开发者从根本上避免并高效解决循环依赖,构建健壮可维护的Spring应用。
`@Validated`注解不工作让你头疼?别急!我们为你准备了一份超实用的排查指南。从依赖缺失到代理失效,从注解位置到嵌套对象,逐一揭示其失效的隐秘角落。告别盲目调试,轻松掌握高效解决方案,让你的代码校验体系坚不可摧。