当前位置: 首页 > news >正文

学习日报|Spring 全局异常与自定义异常拦截器执行顺序问题及解决

在 Spring Web 应用中,异常拦截机制是保障系统稳定性与用户体验的重要一环。然而,在同时存在全局异常拦截器与自定义异常拦截器时,常常会出现执行顺序与预期不符的情况。本文将从原理入手,分析问题并给出可行的解决方案。


一、问题背景

Spring 默认会对 @ControllerAdvice 标注的异常拦截器进行排序。排序逻辑是基于 类名的 ASCII 码顺序

例如:

  • 全局异常拦截器类名以 A 开头

  • 自定义异常拦截器类名以 B 开头

由于 A 的 ASCII 码小于 B,Spring 会优先加载并使用 A 开头的全局异常拦截器。

👉 这就导致实际业务中出现“全国法先于地方法”的不合理情况。


二、期望逻辑

在实际业务开发中,通常更希望:

  • 带有包范围限制的拦截器(@ControllerAdvice(basePackages=...))优先执行

  • 全局异常拦截器作为兜底

就像现实生活中的法律规则:

  • 地方法:针对特定范围更精准,应该优先执行。

  • 全国法:兜底处理,避免遗漏。


三、Spring 默认排序逻辑

Spring 内部在 ExceptionHandlerExceptionResolver 中,会扫描 @ControllerAdvice 并构建 ControllerAdviceBean 列表。
默认情况下,这个列表会根据类名 ASCII 排序,而不考虑是否指定了包范围。

代码简化示意:

List<ControllerAdviceBean> adviceBeans =ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());adviceBeans.sort((bean1, bean2) -> bean1.toString().compareTo(bean2.toString()));

这种排序方式没有考虑“地方法优先”的需求。


四、解决思路

1. 改造排序逻辑

在初始化 ControllerAdviceBean 缓存时,识别出是否存在 选择器(selectors),即是否指定了 basePackagesassignableTypes 等限制条件。

带有选择器的 Advice → 判定为 自定义拦截器(地方法),应当优先。

示例代码:

adviceBeans.sort((ControllerAdviceBean bean1, ControllerAdviceBean bean2) -> {boolean hasSelectors1 = hasSelectors(bean1);boolean hasSelectors2 = hasSelectors(bean2);if (hasSelectors1 && !hasSelectors2) {// bean1 有范围限制 → 优先return -1;} else if (!hasSelectors1 && hasSelectors2) {// bean2 有范围限制 → 优先return 1;} else {// 两者都有或都没有 → 保持原有顺序return 0;}
});

2. 逻辑解读

  • 有包范围限制的拦截器 → 排序时优先放到顶层

  • 全局拦截器 → 放在后面作为兜底

  • 二者都有或都没有 → 保持原始顺序(仍然遵循 ASCII 或 @Order

这样就实现了“地方法优先,全国法兜底”。


五、效果验证

  • com.example.specific 包下的控制器抛出异常时,会优先命中 自定义异常拦截器

  • 其他包的异常,或未被匹配的情况,则交由 全局异常拦截器 兜底。


六、总结

  • Spring 默认的异常拦截器排序逻辑基于 类名 ASCII,可能导致“全局拦截器优先”。

  • 通过修改底层排序逻辑,将 带包范围限制的拦截器提升优先级,即可实现“地方法优先,全国法兜底”。

  • 实际应用中,建议同时结合 @Order 注解,保证拦截器顺序更加可控。


这样一来,异常治理逻辑就符合了业务预期:
精准优先(地方法) + 全局兜底(全国法) → 系统更健壮,也更灵活。


要不要我帮你再写一个 完整的最小 Demo 项目(一个全局拦截器 + 一个包范围拦截器 + 一个触发异常的 Controller),这样你可以直接运行验证排序效果?

http://www.dtcms.com/a/388798.html

相关文章:

  • Spring Boot 参数处理
  • Debian系统基本介绍:新手入门指南
  • Spring Security 框架
  • Qt QPercentBarSeries详解
  • RTT操作系统(3)
  • DNS服务管理
  • IDA Pro配置与笔记
  • 虚函数表在单继承与多继承中的实现机制
  • 矿石生成(1)
  • Linux 线程的概念
  • Unity学习之资源管理(Resources、AssetDatabase、AssetBundle、Addressable)
  • LG P5138 fibonacci Solution
  • 删除UCPD监控服务或者监控驱动
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(33):文法運用第10回1+(考え方14)
  • 向量技术研究报告:从数学基础到AI革命的支柱
  • 802.1x和802.1Q之间关联和作用
  • 基于大模型多模态的人体体型评估:从“尺码测量”到“视觉-感受”范式
  • 更符合人类偏好的具身导航!HALO:面向机器人导航的人类偏好对齐离线奖励学习
  • Transformer多头注意力机制
  • git 分支 error: src refspec sit does not match any`
  • VN1640 CH5 I/O通道终极指南:【VN1630 I/O功能在电源电压时间精确度测试中的深度应用】
  • qt QHorizontalBarSeries详解
  • 半导体制造的芯片可靠性测试的全类别
  • MySQL 索引详解:原理、类型与优化实践
  • AI 重塑就业市场:哪些岗位将被替代?又会催生哪些新职业赛道?
  • mysql表分区备份太慢?如何精准“狙击”所需数据?
  • InVEST实践及在生态系统服务供需、固碳、城市热岛、论文写作等实际项目中应用
  • 数据库视图详解
  • C#并行处理CPU/内存监控:用PerformanceCounter实时监控,避免资源过载(附工具类)
  • 数据结构初阶——红黑树的实现(C++)