查看: 29|回覆: 0

[教程] Spring Boot 全局异常处理策略设计之@ExceptionHandler 与 @ControllerAdvice 生效原理源码解析

[複製鏈接]

2

主題

0

回帖

0

積分

热心网友

金币
0
閲讀權限
220
精華
0
威望
0
贡献
0
在線時間
0 小時
註冊時間
2008-9-7
發表於 2026-1-9 09:48:02 | 顯示全部樓層 |閲讀模式

Spring Boot 全局异常处理策略设计(三):@ExceptionHandler 与 @ControllerAdvice 生效原理源码解析

1. 从一个常见疑问说起

很多人在使用全局异常处理时,都会遇到类似问题:

  • 为什么这个异常没进我的 @ControllerAdvice?
  • 多个 @ExceptionHandler 时,哪个先生效?
  • 参数、返回值为什么能自动解析?
  • 为什么同一个异常,在不同 Controller 表现不一样?

这些问题,用“注解怎么写”是回答不了的,只能从源码解释

2. ExceptionHandlerExceptionResolver 是谁在干活

在上一篇中我们已经知道,异常最终会进入这条责任链:

ExceptionHandlerExceptionResolver
→ ResponseStatusExceptionResolver
→ DefaultHandlerExceptionResolver

@ExceptionHandler 和 @ControllerAdvice 的真正执行者,就是:

ExceptionHandlerExceptionResolver

3. ExceptionHandlerExceptionResolver 的核心职责

从类注释就能看出它的定位:

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

它只做一件事:

找到能处理当前异常的 @ExceptionHandler 方法,并执行它

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

4.1 初始化阶段:扫描所有异常处理方法

在容器启动阶段,ExceptionHandlerExceptionResolver 会执行初始化逻辑:

public void afterPropertiesSet() {
    initExceptionHandlerAdviceCache();
}

4.2 扫描 @ControllerAdvice

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

关键点:

  • 扫描整个 Spring 容器
  • 找出所有标注了 @ControllerAdvice 的 Bean
  • 封装为 ControllerAdviceBean

4.3 ControllerAdvice 的“作用范围”不是全局那么简单

@ControllerAdvice 支持条件匹配:

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

源码中通过 HandlerTypePredicate 判断是否适用当前 Controller。

👉 这也是为什么:

有些 Advice 明明存在,却对某些 Controller 不生效

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

5.1 ExceptionHandlerMethodResolver

每一个 Controller 或 Advice,都会对应一个解析器:

new ExceptionHandlerMethodResolver(beanType);

它会:

  • 扫描所有方法
  • 找出 @ExceptionHandler
  • 建立异常类型 → 方法的映射关系

5.2 一个方法可以处理多个异常

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

源码中会把它拆解成多条映射关系。

5.3 异常匹配是“最近优先”

如果存在继承关系:

RuntimeException
└── IllegalArgumentException

IllegalArgumentException 会优先匹配,而不是父类异常。

这是通过 ExceptionDepthComparator 实现的。

6. 异常发生时,Resolver 是如何找方法的

异常真正发生后,会进入:

protected ModelAndView doResolveHandlerMethodException(...)

核心逻辑:

  1. 先找 Controller 内部的 @ExceptionHandler
  2. 再找全局 @ControllerAdvice
  3. 找到就执行,找不到返回 null

6.1 Controller 内部优先于 ControllerAdvice

这是一个非常重要的优先级规则:

局部异常处理 > 全局异常处理

源码中体现为:

getExceptionHandlerMethod(handlerMethod, exception)

先基于当前 Controller 查找。

7. @ExceptionHandler 方法是如何被执行的

一旦找到目标方法,Spring 会把它包装成:

ServletInvocableHandlerMethod

这个类你在 MVC 参数解析中已经见过。

7.1 参数是如何自动注入的

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

参数解析复用的正是:

  • HandlerMethodArgumentResolver 体系

👉 异常处理方法,本质上也是一个 MVC 方法。

7.2 返回值是如何写入响应的

返回值处理同样复用:

  • HandlerMethodReturnValueHandler
  • HttpMessageConverter

所以你可以:

  • 返回对象
  • 返回 ResponseEntity
  • 返回 void

8. 为什么 @ResponseBody 能生效

在 Spring Boot 中,常见写法是:

@RestControllerAdvice

它本质等价于:

@ControllerAdvice
@ResponseBody

@ResponseBody 的解析发生在:

  • 返回值处理阶段
  • 由 RequestResponseBodyMethodProcessor 完成

9. 多个 @ControllerAdvice 的执行顺序

9.1 顺序规则

优先级由以下规则决定:

  1. @Order
  2. Ordered 接口
  3. 默认顺序(最低优先级)
@Order(1)
@RestControllerAdvice
class BizExceptionAdvice {}
@Order(2)
@RestControllerAdvice
class SystemExceptionAdvice {}

9.2 为什么顺序很重要

因为:

  • 第一个匹配成功的异常处理方法会直接返回
  • 后续 Advice 不再执行

10. 常见“异常不生效”的根本原因

现象根本原因
Advice 不生效basePackages 不匹配
方法不进异常类型不匹配
被吞掉前面 Resolver 已处理
返回 500Resolver 返回 null

这些问题,只有看源码才能彻底理解

11. 异常处理方法执行流程图

图1 @ExceptionHandler 方法解析与执行流程

12. 本篇关键认知升级

到这里,你应该已经清楚:

  1. @ExceptionHandler 并不是“魔法”
  2. ControllerAdvice 不是“全局兜底”,而是有严格匹配规则
  3. 异常处理方法本质上是一个 MVC Handler
  4. Resolver 链决定了异常的最终命运

13. 下一篇预告

到目前为止,我们讲的还是 Spring MVC 层面的异常
但在 Spring Boot 中,还有一个绕不开的存在:

/error

  • 它什么时候被触发?
  • 为什么有的异常进了 /error,而不是 ControllerAdvice?
  • ErrorController、ErrorAttributes 是干什么的?

👉 下一篇,我们正式进入 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

到此这篇关于Spring Boot 全局异常处理策略设计之@ExceptionHandler 与 @ControllerAdvice 生效原理源码解析的文章就介绍到这了,更多相关Spring Boot 全局异常内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!

您可能感兴趣的文章:
  • springboot全局异常处理方式@ControllerAdvice和@ExceptionHandler
  • SpringBoot @ExceptionHandler与@ControllerAdvice异常处理详解
  • SpringBoot的全局异常拦截实践过程
  • springboot全局异常处理方式
  • Spring Boot全局异常处理实战指南
  • Java后端Spring Boot全局异常处理最佳实践记录
  • SpringBoot全局异常处理方式
回覆

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 立即注册

本版積分規則

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部