最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 全局異常處理策略設(shè)計之@ExceptionHandler 與 @ControllerAdvice 生效原理源碼解析

 更新時間:2026年01月09日 09:48:02   作者:Qiuner  
SpringBoot全局異常處理策略設(shè)計@ExceptionHandler與@ControllerAdvice生效原理源碼解析,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

Spring Boot 全局異常處理策略設(shè)計(三):@ExceptionHandler 與 @ControllerAdvice 生效原理源碼解析

1. 從一個常見疑問說起

很多人在使用全局異常處理時,都會遇到類似問題:

  • 為什么這個異常沒進(jìn)我的 @ControllerAdvice?
  • 多個 @ExceptionHandler 時,哪個先生效?
  • 參數(shù)、返回值為什么能自動解析?
  • 為什么同一個異常,在不同 Controller 表現(xiàn)不一樣?

這些問題,用“注解怎么寫”是回答不了的,只能從源碼解釋。

2. ExceptionHandlerExceptionResolver 是誰在干活

在上一篇中我們已經(jīng)知道,異常最終會進(jìn)入這條責(zé)任鏈:

ExceptionHandlerExceptionResolver
→ ResponseStatusExceptionResolver
→ DefaultHandlerExceptionResolver

@ExceptionHandler 和 @ControllerAdvice 的真正執(zhí)行者,就是:

ExceptionHandlerExceptionResolver

3. ExceptionHandlerExceptionResolver 的核心職責(zé)

從類注釋就能看出它的定位:

/**
 * An {@link HandlerExceptionResolver} that resolves exceptions through
 * {@link ExceptionHandler} methods.
 */

它只做一件事:

找到能處理當(dāng)前異常的 @ExceptionHandler 方法,并執(zhí)行它

4. @ExceptionHandler 方法是如何被掃描的

4.1 初始化階段:掃描所有異常處理方法

在容器啟動階段,ExceptionHandlerExceptionResolver 會執(zhí)行初始化邏輯:

public void afterPropertiesSet() {
    initExceptionHandlerAdviceCache();
}

4.2 掃描 @ControllerAdvice

private void initExceptionHandlerAdviceCache() {
    List<ControllerAdviceBean> adviceBeans =
        ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
}

關(guān)鍵點:

  • 掃描整個 Spring 容器
  • 找出所有標(biāo)注了 @ControllerAdvice 的 Bean
  • 封裝為 ControllerAdviceBean

4.3 ControllerAdvice 的“作用范圍”不是全局那么簡單

@ControllerAdvice 支持條件匹配:

@ControllerAdvice(
    basePackages = "com.example.web",
    annotations = RestController.class
)

源碼中通過 HandlerTypePredicate 判斷是否適用當(dāng)前 Controller。

?? 這也是為什么:

有些 Advice 明明存在,卻對某些 Controller 不生效

5. @ExceptionHandler 方法是如何被緩存的

5.1 ExceptionHandlerMethodResolver

每一個 Controller 或 Advice,都會對應(yīng)一個解析器:

new ExceptionHandlerMethodResolver(beanType);

它會:

  • 掃描所有方法
  • 找出 @ExceptionHandler
  • 建立異常類型 → 方法的映射關(guān)系

5.2 一個方法可以處理多個異常

@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
public ErrorResponse handle(Exception e) {}

源碼中會把它拆解成多條映射關(guān)系。

5.3 異常匹配是“最近優(yōu)先”

如果存在繼承關(guān)系:

RuntimeException
└── IllegalArgumentException

IllegalArgumentException 會優(yōu)先匹配,而不是父類異常。

這是通過 ExceptionDepthComparator 實現(xiàn)的。

6. 異常發(fā)生時,Resolver 是如何找方法的

異常真正發(fā)生后,會進(jìn)入:

protected ModelAndView doResolveHandlerMethodException(...)

核心邏輯:

  1. 先找 Controller 內(nèi)部的 @ExceptionHandler
  2. 再找全局 @ControllerAdvice
  3. 找到就執(zhí)行,找不到返回 null

6.1 Controller 內(nèi)部優(yōu)先于 ControllerAdvice

這是一個非常重要的優(yōu)先級規(guī)則:

局部異常處理 > 全局異常處理

源碼中體現(xiàn)為:

getExceptionHandlerMethod(handlerMethod, exception)

先基于當(dāng)前 Controller 查找。

7. @ExceptionHandler 方法是如何被執(zhí)行的

一旦找到目標(biāo)方法,Spring 會把它包裝成:

ServletInvocableHandlerMethod

這個類你在 MVC 參數(shù)解析中已經(jīng)見過。

7.1 參數(shù)是如何自動注入的

@ExceptionHandler(Exception.class)
public ErrorResponse handle(
    Exception ex,
    HttpServletRequest request
) {}

參數(shù)解析復(fù)用的正是:

  • HandlerMethodArgumentResolver 體系

?? 異常處理方法,本質(zhì)上也是一個 MVC 方法。

7.2 返回值是如何寫入響應(yīng)的

返回值處理同樣復(fù)用:

  • HandlerMethodReturnValueHandler
  • HttpMessageConverter

所以你可以:

  • 返回對象
  • 返回 ResponseEntity
  • 返回 void

8. 為什么 @ResponseBody 能生效

在 Spring Boot 中,常見寫法是:

@RestControllerAdvice

它本質(zhì)等價于:

@ControllerAdvice
@ResponseBody

@ResponseBody 的解析發(fā)生在:

  • 返回值處理階段
  • 由 RequestResponseBodyMethodProcessor 完成

9. 多個 @ControllerAdvice 的執(zhí)行順序

9.1 順序規(guī)則

優(yōu)先級由以下規(guī)則決定:

  1. @Order
  2. Ordered 接口
  3. 默認(rèn)順序(最低優(yōu)先級)
@Order(1)
@RestControllerAdvice
class BizExceptionAdvice {}
@Order(2)
@RestControllerAdvice
class SystemExceptionAdvice {}

9.2 為什么順序很重要

因為:

  • 第一個匹配成功的異常處理方法會直接返回
  • 后續(xù) Advice 不再執(zhí)行

10. 常見“異常不生效”的根本原因

現(xiàn)象根本原因
Advice 不生效basePackages 不匹配
方法不進(jìn)異常類型不匹配
被吞掉前面 Resolver 已處理
返回 500Resolver 返回 null

這些問題,只有看源碼才能徹底理解。

11. 異常處理方法執(zhí)行流程圖

圖1 @ExceptionHandler 方法解析與執(zhí)行流程

12. 本篇關(guān)鍵認(rèn)知升級

到這里,你應(yīng)該已經(jīng)清楚:

  1. @ExceptionHandler 并不是“魔法”
  2. ControllerAdvice 不是“全局兜底”,而是有嚴(yán)格匹配規(guī)則
  3. 異常處理方法本質(zhì)上是一個 MVC Handler
  4. Resolver 鏈決定了異常的最終命運(yùn)

13. 下一篇預(yù)告

到目前為止,我們講的還是 Spring MVC 層面的異常。
但在 Spring Boot 中,還有一個繞不開的存在:

/error

  • 它什么時候被觸發(fā)?
  • 為什么有的異常進(jìn)了 /error,而不是 ControllerAdvice?
  • ErrorController、ErrorAttributes 是干什么的?

?? 下一篇,我們正式進(jìn)入 Spring Boot 的異常“二次封裝世界”。

參考資料

  • Spring Framework Reference – Exception Handling
    https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html
  • ExceptionHandlerExceptionResolver 源碼
    https://github.com/spring-projects/spring-framework

到此這篇關(guān)于Spring Boot 全局異常處理策略設(shè)計之@ExceptionHandler 與 @ControllerAdvice 生效原理源碼解析的文章就介紹到這了,更多相關(guān)Spring Boot 全局異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis之動態(tài)SQL和緩存使用及說明

    MyBatis之動態(tài)SQL和緩存使用及說明

    這篇文章主要介紹了MyBatis之動態(tài)SQL和緩存使用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2026-05-05
  • 解決idea打包成功但是resource下的文件沒有成功的問題

    解決idea打包成功但是resource下的文件沒有成功的問題

    這篇文章主要介紹了解決idea打包成功但是resource下的文件沒有成功的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解Java關(guān)于JDK中時間日期的API

    詳解Java關(guān)于JDK中時間日期的API

    這篇文章主要介紹了詳解Java關(guān)于JDK中時間日期的API,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法

    SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法,文中通過實例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-07-07
  • 基于hashmap 的擴(kuò)容和樹形化全面分析

    基于hashmap 的擴(kuò)容和樹形化全面分析

    這篇文章主要介紹了hashmap 的擴(kuò)容和樹形化的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • LogBack+MybatisPlus實現(xiàn)打印sql日志

    LogBack+MybatisPlus實現(xiàn)打印sql日志

    本文主要介紹了LogBack+MybatisPlus實現(xiàn)打印sql日志,設(shè)置異步appender實現(xiàn)按天滾動切割日志,保留3天歷史并自動清理,替代P6Spy避免日志過大,同時需配置MybatisPlus日志輸出路徑,并區(qū)分MyBatis與MyBatisPlus日志輸出
    2025-09-09
  • 教你怎么用Java數(shù)組和鏈表實現(xiàn)棧

    教你怎么用Java數(shù)組和鏈表實現(xiàn)棧

    本篇文章為大家詳細(xì)介紹了怎么用Java數(shù)組和鏈表實現(xiàn)棧,文中有非常詳細(xì)的代碼示例及注釋,對正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • 詳解5種Java中常見限流算法

    詳解5種Java中常見限流算法

    在高并發(fā)系統(tǒng)中,出于系統(tǒng)保護(hù)角度考慮,通常會對流量進(jìn)行限流;不但在工作中要頻繁使用,而且也是面試中的高頻考點。本文就為大家整理了5種Java中常見限流算法,需要的可以參考一下
    2023-04-04
  • java的NIO管道用法代碼分享

    java的NIO管道用法代碼分享

    這篇文章主要介紹了java的NIO管道用法代碼分享,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • Springboot發(fā)送post請求的幾種方式總結(jié)

    Springboot發(fā)送post請求的幾種方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Springboot發(fā)送post請求的幾種方式,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以了解一下
    2024-01-01

最新評論

盐源县| 万源市| 会泽县| 堆龙德庆县| 建水县| 德阳市| 柳河县| 四平市| 古丈县| 红安县| 阳曲县| 彭阳县| 龙川县| 长白| 千阳县| 靖西县| 蚌埠市| 石渠县| 建阳市| 延津县| 简阳市| 突泉县| 永济市| 遂溪县| 确山县| 旺苍县| 仙桃市| 长垣县| 黄大仙区| 阿城市| 大田县| 社旗县| 西畴县| 东乡族自治县| 阿巴嘎旗| 永定县| 香格里拉县| 金山区| 西城区| 阿克| 南丰县|