文章目录
- [Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析](#Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析)
-
- [1. 为什么一定要从 DispatcherServlet 讲起](#1. 为什么一定要从 DispatcherServlet 讲起)
- [2. DispatcherServlet 在请求中的角色定位](#2. DispatcherServlet 在请求中的角色定位)
- [3. doDispatch:异常真正被捕获的地方](#3. doDispatch:异常真正被捕获的地方)
-
- [3.1 doDispatch 的整体结构(简化)](#3.1 doDispatch 的整体结构(简化))
- [3.2 Throwable 为什么会被单独捕获?](#3.2 Throwable 为什么会被单独捕获?)
- [4. processDispatchResult:异常处理的真正入口](#4. processDispatchResult:异常处理的真正入口)
- [5. processHandlerException:责任链的起点](#5. processHandlerException:责任链的起点)
- [6. HandlerExceptionResolver 责任链模型](#6. HandlerExceptionResolver 责任链模型)
-
- [6.1 接口定义](#6.1 接口定义)
- [6.2 默认的三个异常解析器](#6.2 默认的三个异常解析器)
- [7. Resolver 链的执行顺序是如何确定的](#7. Resolver 链的执行顺序是如何确定的)
- [8. 异常是如何被"吃掉"的?](#8. 异常是如何被“吃掉”的?)
- [9. 如果所有 Resolver 都不处理会怎样?](#9. 如果所有 Resolver 都不处理会怎样?)
- [10. 异常责任链流程图](#10. 异常责任链流程图)
- [11. 为什么说这是一个"非常优雅的设计"](#11. 为什么说这是一个“非常优雅的设计”)
- [12. 本篇关键结论](#12. 本篇关键结论)
- 参考资料
- 结束语

Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析
1. 为什么一定要从 DispatcherServlet 讲起
在第一篇中我们已经明确了一点:
异常不是在 Controller 里被"解决"的,而是在框架层被"接管"的。
在 Spring MVC 中,这个"接管者"只有一个入口:
DispatcherServlet
无论你使用的是:
- @ExceptionHandler
- @ControllerAdvice
- @ResponseStatus
- Spring Boot 默认的 /error
它们最终都必须经过 DispatcherServlet 的调度与分发。
2. DispatcherServlet 在请求中的角色定位
在一次完整的请求处理中,DispatcherServlet 的职责可以概括为四步:
- 查找 Handler
- 执行 Handler
- 处理返回值
- 处理异常
异常并不是一个"补丁逻辑",而是 DispatcherServlet 的标准流程之一。
3. doDispatch:异常真正被捕获的地方
DispatcherServlet 的核心方法是 doDispatch,异常处理的关键逻辑就在这里。
3.1 doDispatch 的整体结构(简化)
java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
try {
// 1. 查找 Handler
// 2. 执行 Handler
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new ServletException(err);
}
processDispatchResult(request, response, handler, dispatchException);
}
非常重要的一点:
DispatcherServlet 并不会在 catch 中直接处理异常,而是统一交给
processDispatchResult。
3.2 Throwable 为什么会被单独捕获?
java
catch (Throwable err) {
dispatchException = new ServletException(err);
}
这里体现了一个非常关键的设计思想:
- 框架不允许 Throwable 直接向外传播
- 所有异常最终都会被"标准化"为 Exception
这保证了后续异常解析链的统一性。
4. processDispatchResult:异常处理的真正入口
java
private void processDispatchResult(
HttpServletRequest request,
HttpServletResponse response,
HandlerExecutionChain mappedHandler,
Exception exception) {
if (exception != null) {
mv = processHandlerException(request, response, handler, exception);
}
}
只要 exception != null,就会进入异常处理流程。
5. processHandlerException:责任链的起点
java
protected ModelAndView processHandlerException(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
ModelAndView mv = resolver.resolveException(request, response, handler, ex);
if (mv != null) {
return mv;
}
}
throw ex;
}
这一段代码,是 Spring MVC 异常机制的灵魂。
从中可以明确看出三点:
- 异常处理是一个 Resolver 链
- 按顺序逐个尝试解析
- 谁先返回非 null,谁就"吃掉"异常
6. HandlerExceptionResolver 责任链模型
6.1 接口定义
java
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);
}
设计目的非常明确:
给异常一个"翻译成响应"的机会
6.2 默认的三个异常解析器
Spring MVC 默认注册了三个 Resolver:
| Resolver | 作用 |
|---|---|
| ExceptionHandlerExceptionResolver | 处理 @ExceptionHandler |
| ResponseStatusExceptionResolver | 处理 @ResponseStatus |
| DefaultHandlerExceptionResolver | 处理 Spring 内置异常 |
它们构成了一条有顺序、有分工、有兜底的异常责任链。
7. Resolver 链的执行顺序是如何确定的
Resolver 并不是写死的,而是通过初始化流程注入:
java
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
最终顺序为:
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
顺序的设计逻辑是:
- 用户自定义优先
- 注解语义其次
- 框架兜底最后
8. 异常是如何被"吃掉"的?
当某个 Resolver 返回了非 null 的 ModelAndView:
java
if (mv != null) {
return mv;
}
意味着:
- 异常被成功解析
- 后续 Resolver 不再执行
- DispatcherServlet 不会再抛出异常
这也是为什么:
一个异常只会被一个 Resolver 处理
9. 如果所有 Resolver 都不处理会怎样?
java
throw ex;
结果是:
- 异常继续向上抛
- 对 Servlet 容器来说,这是一个未处理异常
- 在 Spring Boot 中,通常会被 /error 接管(后续篇章重点)
10. 异常责任链流程图
throw Exception
未处理
未处理
未处理
Controller 执行
DispatcherServlet
processHandlerException
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
异常继续抛出
图1 Spring MVC 异常解析责任链流程图
11. 为什么说这是一个"非常优雅的设计"
从源码可以清楚看到:
- 没有 if-else 地狱
- 没有硬编码异常类型
- 完全遵循 开闭原则
你可以:
- 插入自定义 Resolver
- 调整顺序
- 替换默认行为
而 DispatcherServlet 不需要修改一行代码。
12. 本篇关键结论
到这一篇为止,我们已经明确:
- DispatcherServlet 是异常处理的唯一入口
- 异常处理不是一个方法,而是一条责任链
- @ExceptionHandler 只是其中一个 Resolver
- Spring MVC 把"异常 → 响应"的逻辑彻底解耦
但还有几个绕不开的问题:
- @ExceptionHandler 是如何被扫描并匹配异常的?
- @ControllerAdvice 为什么能全局生效?
- ResponseBody 是如何写入响应的?
- Spring Boot 为什么要额外引入 /error?
👉 这些问题,必须进入 Resolver 内部才能解释清楚。
参考资料
- Spring Framework Reference -- Exception Handling
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html - DispatcherServlet 源码
https://github.com/spring-projects/spring-framework
结束语

掘金点击访问Qiuner CSDN点击访问Qiuner GitHub点击访问Qiuner Gitee点击访问Qiuner
| 专栏 | 简介 |
|---|---|
| 📊 一图读懂系列 | 图文并茂,轻松理解复杂概念 |
| 📝 一文读懂系列 | 深入浅出,全面解析技术要点 |
| 🌟持续更新 | 保持学习,不断进步 |
| 🎯 人生经验 | 经验分享,共同成长 |
你好,我是Qiuner. 为帮助别人少走弯路而写博客
如果本篇文章帮到了你 不妨点个赞 吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。
代码都在Github或Gitee上,如有需要可以去上面自行下载。记得给我点星星哦😍
如果你遇到了问题,自己没法解决,可以去我掘金评论区问。CSDN评论区和私信消息看不完 掘金消息少一点.
| 上一篇推荐 | 链接 |
|---|---|
| Java程序员快又扎实的学习路线 | 点击该处自动跳转查看哦 |
| 一文读懂 AI | 点击该处自动跳转查看哦 |
| 一文读懂 服务器 | 点击该处自动跳转查看哦 |
| 2024年创作回顾 | 点击该处自动跳转查看哦 |
| 一文读懂 ESLint配置 | 点击该处自动跳转查看哦 |
| 老鸟如何追求快捷操作电脑 | 点击该处自动跳转查看哦 |
| 未来会写什么文章? | 预告链接 |
|---|---|
| 一文读懂 XX? | 点击该处自动跳转查看哦 |
| 2025年终总结 | 点击该处自动跳转查看哦 |
| 一图读懂 XX? | 点击该处自动跳转查看哦 |