Spring Boot全局异常处理主干为四步:DispatcherServlet捕获异常并触发processDispatchResult;ExceptionHandlerExceptionResolver匹配@ControllerAdvice中的@ExceptionHandler;@ControllerAdvice支持basePackages等作用域约束;/error是兜底路径,由BasicErrorController根据Accept头返回JSON或HTML。

要看 Spring Boot 全局异常的底层源码原理,关键不是逐行翻代码,而是理清“异常从抛出到响应”这条链路上的几个核心节点及其协作关系。重点看清楚 DispatcherServlet 怎么捕获、ExceptionHandlerExceptionResolver 怎么匹配、@ControllerAdvice 怎么生效、/error 又在什么情况下接管——这四步串起来,就抓住了主干。
DispatcherServlet 是异常捕获的第一道关
所有 Controller 方法执行时若抛出未捕获异常,都会被 DispatcherServlet 的 doDispatch() 方法捕获。它不处理异常,只做两件事:
- 把异常赋值给
dispatchException局部变量 - 调用
processDispatchResult()进入统一异常处理入口
这个方法是整个 MVC 异常流程的真正起点,后续所有解析器都由它触发。
ExceptionHandlerExceptionResolver 负责匹配 @ExceptionHandler
它是 @ControllerAdvice + @ExceptionHandler 背后的执行引擎。启动时会扫描所有标注 @ControllerAdvice 的 Bean,并缓存其中的 @ExceptionHandler 方法(按异常类型建立映射表)。当 processDispatchResult 调用它时,它会:
- 遍历所有已缓存的异常处理器方法
- 用 HandlerTypePredicate 判断该 advice 是否适用于当前 Controller(比如 basePackages 或 annotations 限制)
- 再按异常类型继承关系匹配最具体的 @ExceptionHandler 方法(如 ArithmeticException 比 Exception 更优先)
- 匹配成功后,用 WebDataBinder 等组件解析参数、渲染返回值(如 ResponseEntity 或 String 视图名)
@ControllerAdvice 不是“无条件全局”,有作用范围约束
很多人以为加了 @ControllerAdvice 就一定生效,其实它支持精细控制:
-
basePackages = "com.example.api":只对指定包下的 Controller 生效 -
annotations = RestController.class:只增强带该注解的类 -
assignableTypes = {UserController.class}:只针对特定 Controller 类型 - 不写任何条件,默认作用于所有 @Controller / @RestController
这些条件都在初始化阶段封装进 ControllerAdviceBean,并在每次匹配时动态校验。
/error 是兜底出口,不是默认路径
如果所有 HandlerExceptionResolver 都没返回 ModelAndView(即全部返回 null),DispatcherServlet 就放弃 MVC 层处理,直接向 Servlet 容器发起请求转发:request.getRequestDispatcher("/error").forward(request, response)。这时 Spring Boot 的 BasicErrorController 才介入,它:
- 不解析异常,只读取
ErrorAttributes中预存的错误信息(如 status、message、timestamp) - 根据 Accept 请求头决定返回 JSON 还是 HTML(浏览器访问显示 Whitelabel 页面,API 调用返回 JSON)
- 本质是容器级 fallback,说明你的 @ExceptionHandler 没覆盖到该异常或被更高优先级 resolver 拦截了


















