Spring Boot全局異常處理機制中DispatcherServlet的處理流程和作用
1. 為什么一定要從 DispatcherServlet 講起
異常不是在 Controller 里被“解決”的,而是在框架層被“接管”的。
在 Spring MVC 中,這個“接管者”只有一個入口:
DispatcherServlet
無論你使用的是:
- @ExceptionHandler
- @ControllerAdvice
- @ResponseStatus
- Spring Boot 默認的 /error
它們最終都必須經(jīng)過 DispatcherServlet 的調(diào)度與分發(fā)。
2. DispatcherServlet 在請求中的角色定位
在一次完整的請求處理中,DispatcherServlet 的職責可以概括為四步:
- 查找 Handler
- 執(zhí)行 Handler
- 處理返回值
- 處理異常
異常并不是一個“補丁邏輯”,而是 DispatcherServlet 的標準流程之一。
3. doDispatch:異常真正被捕獲的地方
DispatcherServlet 的核心方法是 doDispatch,異常處理的關(guān)鍵邏輯就在這里。
3.1 doDispatch 的整體結(jié)構(gòu)(簡化)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
try {
// 1. 查找 Handler
// 2. 執(zhí)行 Handler
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new ServletException(err);
}
processDispatchResult(request, response, handler, dispatchException);
}
非常重要的一點:
DispatcherServlet 并不會在 catch 中直接處理異常,而是統(tǒng)一交給
processDispatchResult。
3.2 Throwable 為什么會被單獨捕獲?
catch (Throwable err) {
dispatchException = new ServletException(err);
}
這里體現(xiàn)了一個非常關(guān)鍵的設(shè)計思想:
- 框架不允許 Throwable 直接向外傳播
- 所有異常最終都會被“標準化”為 Exception
這保證了后續(xù)異常解析鏈的統(tǒng)一性。
4. processDispatchResult:異常處理的真正入口
private void processDispatchResult(
HttpServletRequest request,
HttpServletResponse response,
HandlerExecutionChain mappedHandler,
Exception exception) {
if (exception != null) {
mv = processHandlerException(request, response, handler, exception);
}
}
只要 exception != null,就會進入異常處理流程。
5. processHandlerException:責任鏈的起點
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 接口定義
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);
}
設(shè)計目的非常明確:
給異常一個“翻譯成響應(yīng)”的機會
6.2 默認的三個異常解析器
Spring MVC 默認注冊了三個 Resolver:
| Resolver | 作用 |
|---|---|
| ExceptionHandlerExceptionResolver | 處理 @ExceptionHandler |
| ResponseStatusExceptionResolver | 處理 @ResponseStatus |
| DefaultHandlerExceptionResolver | 處理 Spring 內(nèi)置異常 |
它們構(gòu)成了一條有順序、有分工、有兜底的異常責任鏈。
7. Resolver 鏈的執(zhí)行順序是如何確定的
Resolver 并不是寫死的,而是通過初始化流程注入:
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
最終順序為:
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
順序的設(shè)計邏輯是:
- 用戶自定義優(yōu)先
- 注解語義其次
- 框架兜底最后
8. 異常是如何被“吃掉”的?
當某個 Resolver 返回了非 null 的 ModelAndView:
if (mv != null) {
return mv;
}
意味著:
- 異常被成功解析
- 后續(xù) Resolver 不再執(zhí)行
- DispatcherServlet 不會再拋出異常
這也是為什么:
一個異常只會被一個 Resolver 處理
9. 如果所有 Resolver 都不處理會怎樣?
throw ex;
結(jié)果是:
- 異常繼續(xù)向上拋
- 對 Servlet 容器來說,這是一個未處理異常
- 在 Spring Boot 中,通常會被 /error 接管(后續(xù)篇章重點)
10. 異常責任鏈流程圖

圖1 Spring MVC 異常解析責任鏈流程圖
11. 為什么說這是一個“非常優(yōu)雅的設(shè)計”
從源碼可以清楚看到:
- 沒有 if-else 地獄
- 沒有硬編碼異常類型
- 完全遵循 開閉原則
你可以:
- 插入自定義 Resolver
- 調(diào)整順序
- 替換默認行為
而 DispatcherServlet 不需要修改一行代碼。
12. 本篇關(guān)鍵結(jié)論
到這一篇為止,我們已經(jīng)明確:
- DispatcherServlet 是異常處理的唯一入口
- 異常處理不是一個方法,而是一條責任鏈
- @ExceptionHandler 只是其中一個 Resolver
- Spring MVC 把“異常 → 響應(yīng)”的邏輯徹底解耦
但還有幾個繞不開的問題:
- @ExceptionHandler 是如何被掃描并匹配異常的?
- @ControllerAdvice 為什么能全局生效?
- ResponseBody 是如何寫入響應(yīng)的?
- Spring Boot 為什么要額外引入 /error?
?? 這些問題,必須進入 Resolver 內(nèi)部才能解釋清楚。
到此這篇關(guān)于Spring Boot全局異常處理機制中DispatcherServlet的處理流程和作用的文章就介紹到這了,更多相關(guān)Spring Boot異常處理機制DispatcherServlet內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
參考資料
- 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
相關(guān)文章
Springboot項目的服務(wù)器部署與發(fā)布方式
本文記錄了將Springboot項目部署到服務(wù)器并發(fā)布的過程,包括在IDEA中打包、選擇服務(wù)器、連接服務(wù)器、安裝環(huán)境、上傳jar包、配置環(huán)境變量以及運行項目等步驟2025-03-03
Java實現(xiàn)跳轉(zhuǎn)到指定頁面的方法小結(jié)
在Java中,實現(xiàn)頁面跳轉(zhuǎn)主要涉及到Web開發(fā),而這通常通過使用Java的Web框架(如Servlet、Spring MVC)來完成,下面講解一下如何在不同的Java Web框架中實現(xiàn)頁面跳轉(zhuǎn),文中有詳細的代碼示例供大家參考,需要的朋友可以參考下2024-05-05
JDK1.8源碼下載及idea2021導入jdk1.8源碼的詳細步驟
這篇文章主要介紹了JDK1.8源碼下載及idea2021導入jdk1.8源碼的詳細步驟,在文章開頭就給大家分享了JDK1.8源碼下載地址和下載步驟,告訴大家idea2021.1.3導入JDK1.8源碼步驟,需要的朋友可以參考下2022-11-11
Java線程之間數(shù)據(jù)傳遞的實現(xiàn)示例(4種)
我們經(jīng)常會遇到父子線程數(shù)據(jù)傳遞(非調(diào)用參數(shù))的場景,本文主要介紹了Java線程之間數(shù)據(jù)傳遞的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
完美解決在Servlet中出現(xiàn)一個輸出中文亂碼的問題
下面小編就為大家?guī)硪黄昝澜鉀Q在Servlet中出現(xiàn)一個輸出中文亂碼的問題。小編覺得挺不錯的現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01
如何手動安裝Gradle并配置IDEA使用Gradle構(gòu)建
本文給大家分享手動安裝Gradle并配置IDEA使用Gradle構(gòu)建的步驟,本文給大家介紹的非常詳細,感興趣的朋友一起看看吧2025-04-04

