Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析

Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析

文章目录


Spring Boot 全局异常处理策略设计(二):DispatcherServlet 与异常解析责任链源码解析

1. 为什么一定要从 DispatcherServlet 讲起

在第一篇中我们已经明确了一点:
异常不是在 Controller 里被“解决”的,而是在框架层被“接管”的。

在 Spring MVC 中,这个“接管者”只有一个入口:

DispatcherServlet

无论你使用的是:

  • @ExceptionHandler
  • @ControllerAdvice
  • @ResponseStatus
  • Spring Boot 默认的 /error

它们最终都必须经过 DispatcherServlet 的调度与分发。


2. DispatcherServlet 在请求中的角色定位

在一次完整的请求处理中,DispatcherServlet 的职责可以概括为四步:

  1. 查找 Handler
  2. 执行 Handler
  3. 处理返回值
  4. 处理异常

异常并不是一个“补丁逻辑”,而是 DispatcherServlet 的标准流程之一


3. doDispatch:异常真正被捕获的地方

DispatcherServlet 的核心方法是 doDispatch,异常处理的关键逻辑就在这里。

3.1 doDispatch 的整体结构(简化)

protectedvoiddoDispatch(HttpServletRequest request,HttpServletResponse response){try{// 1. 查找 Handler// 2. 执行 Handler}catch(Exception ex){ dispatchException = ex;}catch(Throwable err){ dispatchException =newServletException(err);}processDispatchResult(request, response, handler, dispatchException);}

非常重要的一点:

DispatcherServlet 并不会在 catch 中直接处理异常,而是统一交给 processDispatchResult

3.2 Throwable 为什么会被单独捕获?

catch(Throwable err){ dispatchException =newServletException(err);}

这里体现了一个非常关键的设计思想:

  • 框架不允许 Throwable 直接向外传播
  • 所有异常最终都会被“标准化”为 Exception

这保证了后续异常解析链的统一性。


4. processDispatchResult:异常处理的真正入口

privatevoidprocessDispatchResult(HttpServletRequest request,HttpServletResponse response,HandlerExecutionChain mappedHandler,Exception exception){if(exception !=null){ mv =processHandlerException(request, response, handler, exception);}}

只要 exception != null,就会进入异常处理流程。


5. processHandlerException:责任链的起点

protectedModelAndViewprocessHandlerException(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 异常机制的灵魂

从中可以明确看出三点:

  1. 异常处理是一个 Resolver 链
  2. 按顺序逐个尝试解析
  3. 谁先返回非 null,谁就“吃掉”异常

6. HandlerExceptionResolver 责任链模型

6.1 接口定义

publicinterfaceHandlerExceptionResolver{ModelAndViewresolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex);}

设计目的非常明确:

给异常一个“翻译成响应”的机会

6.2 默认的三个异常解析器

Spring MVC 默认注册了三个 Resolver:

Resolver作用
ExceptionHandlerExceptionResolver处理 @ExceptionHandler
ResponseStatusExceptionResolver处理 @ResponseStatus
DefaultHandlerExceptionResolver处理 Spring 内置异常

它们构成了一条有顺序、有分工、有兜底的异常责任链


7. Resolver 链的执行顺序是如何确定的

Resolver 并不是写死的,而是通过初始化流程注入:

this.handlerExceptionResolvers =getDefaultStrategies(context,HandlerExceptionResolver.class);

最终顺序为:

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

顺序的设计逻辑是:

  • 用户自定义优先
  • 注解语义其次
  • 框架兜底最后

8. 异常是如何被“吃掉”的?

当某个 Resolver 返回了非 null 的 ModelAndView:

if(mv !=null){return mv;}

意味着:

  • 异常被成功解析
  • 后续 Resolver 不再执行
  • DispatcherServlet 不会再抛出异常

这也是为什么:

一个异常只会被一个 Resolver 处理

9. 如果所有 Resolver 都不处理会怎样?

throw ex;

结果是:

  • 异常继续向上抛
  • 对 Servlet 容器来说,这是一个未处理异常
  • 在 Spring Boot 中,通常会被 /error 接管(后续篇章重点)

10. 异常责任链流程图

throw Exception

未处理

未处理

未处理

Controller 执行

DispatcherServlet

processHandlerException

ExceptionHandlerExceptionResolver

ResponseStatusExceptionResolver

DefaultHandlerExceptionResolver

异常继续抛出

图1 Spring MVC 异常解析责任链流程图


11. 为什么说这是一个“非常优雅的设计”

从源码可以清楚看到:

  • 没有 if-else 地狱
  • 没有硬编码异常类型
  • 完全遵循 开闭原则

你可以:

  • 插入自定义 Resolver
  • 调整顺序
  • 替换默认行为

而 DispatcherServlet 不需要修改一行代码


12. 本篇关键结论

到这一篇为止,我们已经明确:

  1. DispatcherServlet 是异常处理的唯一入口
  2. 异常处理不是一个方法,而是一条责任链
  3. @ExceptionHandler 只是其中一个 Resolver
  4. 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

结束语

掘金点击访问QiunerZEEKLOG点击访问QiunerGitHub点击访问QiunerGitee点击访问Qiuner

专栏简介
📊 一图读懂系列图文并茂,轻松理解复杂概念
📝 一文读懂系列深入浅出,全面解析技术要点
🌟持续更新保持学习,不断进步
🎯 人生经验经验分享,共同成长
你好,我是Qiuner. 为帮助别人少走弯路而写博客

如果本篇文章帮到了你 不妨点个吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。

代码都在Github或Gitee上,如有需要可以去上面自行下载。记得给我点星星哦😍

如果你遇到了问题,自己没法解决,可以去我掘金评论区问。ZEEKLOG评论区和私信消息看不完 掘金消息少一点.
上一篇推荐链接
Java程序员快又扎实的学习路线点击该处自动跳转查看哦
一文读懂 AI点击该处自动跳转查看哦
一文读懂 服务器点击该处自动跳转查看哦
2024年创作回顾点击该处自动跳转查看哦
一文读懂 ESLint配置点击该处自动跳转查看哦
老鸟如何追求快捷操作电脑点击该处自动跳转查看哦
未来会写什么文章?预告链接
一文读懂 XX?点击该处自动跳转查看哦
2025年终总结点击该处自动跳转查看哦
一图读懂 XX?点击该处自动跳转查看哦

Read more

卷积神经网络(CNN)进阶:经典架构解析与实战开发

卷积神经网络(CNN)进阶:经典架构解析与实战开发

卷积神经网络(CNN)进阶:经典架构解析与实战开发 💡 学习目标:掌握CNN的经典进阶架构设计思路,理解不同架构的核心创新点,能够基于经典架构开发定制化图像任务模型。 💡 学习重点:LeNet-5、AlexNet、VGGNet、ResNet的核心结构与改进逻辑,基于PyTorch实现ResNet-50并完成图像分类任务。 49.1 卷积神经网络进阶的核心驱动力 卷积神经网络从最初的简单结构发展到深度模型,核心驱动力是解决深层网络的性能瓶颈和提升特征提取的效率与精度。 在早期CNN的应用中,研究人员发现两个关键问题: 1. 网络深度增加到一定程度后,会出现梯度消失或梯度爆炸问题,导致模型无法收敛。 2. 简单堆叠卷积层的方式,会造成特征冗余和计算资源浪费,模型泛化能力受限。 ⚠️ 注意:CNN的进阶过程不是单纯的“堆层数”,而是通过结构创新、参数优化和训练技巧的结合,实现性能的突破。 ✅ 结论:经典CNN架构的每一次升级,都针对当时的技术痛点提出了创新性解决方案,掌握这些方案的设计思路,比记住网络结构更重要。 49.2 经典CNN架构深度解析 49.2.1

By Ne0inhk
快过年了,写个游戏玩玩,放松下,解析俄罗斯方块游戏(可直接复制代码使用,玩游戏)。罗斯方块游戏技术解析:从前端实现到工程化思考

快过年了,写个游戏玩玩,放松下,解析俄罗斯方块游戏(可直接复制代码使用,玩游戏)。罗斯方块游戏技术解析:从前端实现到工程化思考

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎点赞 + 收藏 + 关注哦 💕 快过年了,写个游戏玩玩,放松下,解析俄罗斯方块游戏(可直接复制代码,玩游戏)。罗斯方块游戏技术解析:从前端实现到工程化思考 📚 本文简介 本文解析了一个基于HTML5+CSS3+JavaScript的俄罗斯方块网页游戏实现。项目采用模块化设计,包含index.html、style.css和script.js三个核心文件,遵循前端开发最佳实践。HTML结构采用语义化布局,使用Canvas双画布分别渲染主游戏区和预览区。CSS运用Flexbox布局、毛玻璃效果、过渡动画等现代特性,实现响应式设计。JavaScript处理游戏逻辑,包括方块旋转、碰撞检测等核心算法。项目兼顾性能与用户体验,是前端游戏开发的经典案例。全文从架构设计到实现细节进行了深度技术解析。 目录 * 快过年了,写个游戏玩玩,放松下,解析俄罗斯方块游戏(可直接复制代码,玩游戏)。罗斯方块游戏技术解析:

By Ne0inhk

Fish-Speech 1.5 零基础教程:5分钟搭建语音合成WebUI

Fish-Speech 1.5 零基础教程:5分钟搭建语音合成WebUI 想不想拥有一个自己的“AI配音师”?不用下载软件,不用配置复杂环境,5分钟就能在浏览器里生成各种声音。今天,我就带你从零开始,用最简单的方式搭建Fish-Speech 1.5的语音合成WebUI。 Fish-Speech 1.5是个很厉害的语音合成模型,它最大的特点就是“聪明”。传统的语音合成需要依赖复杂的音素规则库,而这个模型能直接理解文本,就像人一样,看到文字就能读出来。它采用了一种创新的双自回归Transformer架构,计算效率高,生成的声音质量也好。 最棒的是,现在有现成的镜像可以直接用,省去了所有安装配置的麻烦。下面我就手把手教你,怎么在5分钟内把它跑起来。 1. 准备工作:理解我们要做什么 在开始之前,我们先简单了解一下这个项目。Fish-Speech 1.5提供了两种使用方式: WebUI(网页界面):这是最推荐的方式。打开浏览器,输入文字,点一下按钮,就能听到生成的声音。界面是中文的,操作起来非常直观,适合大多数人使用。

By Ne0inhk
后端代码不用写了?前端操作数据库?一文精通Supabase,实战教程+本地部署

后端代码不用写了?前端操作数据库?一文精通Supabase,实战教程+本地部署

视频版:https://www.bilibili.com/video/BV1ZJsBznEt3 2025年最火的后端开源项目那必须是Supabase。Supabase是一个开源的后端级服务框架,在强大的PostgreSQL数据库的基础上,封装了用户认证、文件存储、可视化的运维面板等功能,为开发者提供了一整套开箱即用的后端基础设施。Supabase在Github上面有恐怖的9万star,这已经是整个Github上面最顶级的开源项目之一了。 总的来说,Supabase为开发者提供了三大部分的能力:后端、前端与免费的云服务。Supabase在后端提供数据库、文件存储、边缘函数、用户鉴权等各种基础设施。在前端方面,Supabase提供客户端SDK,可以将任何一个前端框架,比如React, Vue,甚至手机APP,用几行代码就可以轻松接入后端。 Supabase是一个完全开源免费的项目,我们可以使用源代码或者docker镜像,自己部署一个Supabase的完整实例。如果懒得自己部署,Supabase的官方还提供一个云服务的版本,我们只需要注册一个账户,就能立即获得一个免费的Supabase

By Ne0inhk