Spring 源码阅读指南 - 沈世钧的文章 - 知乎
https://zhuanlan.zhihu.com/p/51393923
您理解得非常正确。问题的本质很可能是 `@RestControllerAdvice` 的切入点晚于 JSON 解析错误发生的位置。
Spring MVC 的请求处理流程大致如下:
1. HTTP 请求到达 Servlet 容器
2. 经过各种 Servlet 过滤器
3. 到达 Spring 的 `DispatcherServlet`
4. `DispatcherServlet` 将请求委托给 `HttpMessageConverter` 来解析请求体
5. **在这一步进行 JSON 反序列化**,将 JSON 转换为 Java 对象
6. 如果转换成功,请求才会被路由到控制器方法
7. `@RestControllerAdvice` 主要作用于控制器方法执行期间抛出的异常
您遇到的问题是在第 5 步发生的 - JSON 解析错误发生在请求到达控制器之前。虽然 `@RestControllerAdvice` 理论上也可以处理这些"前置"异常,但在某些配置或情况下可能失效。
解决这个问题的方法有:
1. **使用自定义的 HttpMessageConverter**:
您可以创建一个自定义的消息转换器,在其中捕获异常并转换为您的自定义异常。
2. **实现 HandlerExceptionResolver**:
这是一个更底层的接口,可以捕获到更早期的异常。
3. **扩展 ResponseEntityExceptionHandler**:
如我之前提到的,让您的异常处理器继承这个类,它专门设计用来处理这些早期的框架异常。
如果您想快速验证这个问题,可以添加一个简单的测试:
```java
@GetMapping("/test-exception")
public void testException() {
throw new HttpMessageNotReadableException("测试异常", new InvalidFormatException(
null, "测试消息", "s", Long.class));
}
```
如果这个手动抛出的异常能被您的异常处理器捕获,但实际的 JSON 解析错误却不能,那就证实了问题确实出在捕获的时机上。
```Java
Diff:
* changes on com.qunar.dzs.hotelsearch.polaris.domain.entity.task.PreRunTask/ :
- 'createTime' value 'Tue Feb 25 16:25:09 CST 2025' unset
- 'createUser' value 'yishou.liu' unset
- 'modifiedTime' value 'Tue Feb 25 16:26:29 CST 2025' unset
- 'status' value '1' unset
Diff:
* changes on com.qunar.dzs.hotelsearch.polaris.domain.entity.task.PreRunTask/ :
- 'basicGroupsTick' collection changes :
1. 'com.qunar.dzs.hotelsearch.polaris.domain.entity.task.PreRunTask/#basicGroupsTick/5' added
2. 'com.qunar.dzs.hotelsearch.polaris.domain.entity.task.PreRunTask/#basicGroupsTick/6' added
- 'basicGroupsTick/5.canPreRun' = 'true'
- 'basicGroupsTick/5.id' = '88'
- 'basicGroupsTick/5.useModified' = 'true'
- 'basicGroupsTick/6.canPreRun' = 'true'
- 'basicGroupsTick/6.id' = '87'
- 'basicGroupsTick/6.useModified' = 'true'
- 'basicGroupsTickPriority' collection changes :
1. '88' added
2. '87' added
- 'basicGroupsTickPriorityAcked' value 'true' unset
- 'createTime' value 'Tue Feb 25 16:25:09 CST 2025' unset
- 'createUser' value 'yishou.liu' unset
- 'diffGroupsTickPriorityAcked' value 'true' unset
- 'modifiedTime' value 'Tue Feb 25 16:25:20 CST 2025' unset
- 'status' value '1' unset
```