Spring Boot 全局异常处理策略设计(一):异常不只是 try-catch

@[toc]


Spring Boot 全局异常处理策略设计(一):异常不只是 try-catch

1. 为什么异常值得单独写一整个系列

在多数业务代码中,异常往往被当作一种"不得不写的语法":

php 复制代码
 try {
     service.process();
 } catch (Exception e) {
     log.error("error", e);
 }

但在真实的 Web 系统中,异常从来不是一个语法问题,而是一个系统失控时的兜底机制

一次 HTTP 请求,往往会经历如下链路:

  • Filter
  • Interceptor
  • Controller
  • 参数绑定与校验
  • Service
  • DAO
  • 事务
  • AOP 代理
  • JSON 序列化

异常可以在任何一个环节出现,而且一旦出现,就会"逆着调用栈"向上冒泡,最终由框架决定:

  • 是否回滚事务
  • 是否返回 500
  • 返回什么格式
  • 是否记录堆栈
  • 是否暴露错误信息给前端

因此,异常不是"边角料",而是整个调用链的终点汇合处


2. Java 异常模型的关键认知(只讲对后面有用的)

2.1 Throwable 体系结构

php 复制代码
 Throwable
  ├── Error
  └── Exception
       ├── RuntimeException
       └── Checked Exception
  • Error:JVM 级错误,应用通常无能为力
  • Exception:应用级异常
  • RuntimeException:Spring 默认认为这是"不可恢复异常"
  • Checked Exception:需要显式声明和处理

Spring 事务为什么默认只对 RuntimeException 回滚? 这个问题在后面的事务异常章节会专门展开。


2.2 异常传播是"反向调用链"

正常调用是:

复制代码
 Controller → Service → DAO

异常传播是:

复制代码
 DAO 抛异常 → Service → Controller → 框架

谁最后接住异常,谁就拥有最终解释权。


3. 为什么 Web 系统不能到处 try-catch

3.1 try-catch 的三个常见问题

  1. 吞异常,导致问题被掩盖
  2. 重复代码,Controller 层异常处理泛滥
  3. 破坏事务回滚逻辑

例如:

typescript 复制代码
 @Transactional
 public void createOrder() {
     try {
         saveOrder();
     } catch (Exception e) {
         log.error("error", e);
     }
 }

这个代码看起来稳健,实际上事务已经无法回滚


3.2 异常必须"集中处理"

在 Web 架构中,有一个非常重要的设计原则:

异常应该在"边界层"统一处理,而不是在业务层消化。

Spring MVC 正是基于这个原则,设计了一整套异常处理机制。


4. Spring MVC 的异常处理总体设计思想

4.1 正常流程 vs 异常流程

正常流程:

复制代码
 请求 → Handler → 返回值 → 响应

异常流程:

复制代码
 请求 → Handler → 抛异常 → 异常解析 → 响应

Spring MVC 的核心设计点在于:

异常不是 if-else 分支,而是一条独立的处理链路。


4.2 异常处理在 DispatcherServlet 中的位置

DispatcherServlet 是整个 MVC 的"总控中枢"。

在其核心方法 doDispatch 中,异常被统一捕获:

php 复制代码
 try {
     // 查找 Handler 并执行
 } catch (Exception ex) {
     dispatchException = ex;
 }

这意味着:

  • Controller 不需要感知异常如何返回
  • 框架会统一接管异常

5. Spring MVC 的三种基础异常处理方式

5.1 直接抛出异常(推荐)

typescript 复制代码
@GetMapping("/order")
public Order getOrder() {
    throw new IllegalArgumentException("参数错误");
}

异常会交给框架处理,而不是在 Controller 内部解决。


5.2 @ExceptionHandler:局部异常处理

kotlin 复制代码
@RestController
public class OrderController {

    @ExceptionHandler(IllegalArgumentException.class)
    public String handleIllegalArg(Exception e) {
        return e.getMessage();
    }
}

特点:

  • 只对当前 Controller 生效
  • 适合非常局部的异常场景

5.3 @ControllerAdvice:全局异常处理(重点)

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ErrorResponse handle(Exception e) {
        return new ErrorResponse("500", e.getMessage());
    }
}

这是 Spring Boot 项目中最常见的异常处理入口


6. @ControllerAdvice 的设计价值

6.1 为什么它是"全局异常处理"的核心

@ControllerAdvice 本质上解决了三个问题:

  1. 异常集中管理
  2. 返回格式统一
  3. 与业务逻辑解耦

6.2 多个 ControllerAdvice 的顺序问题

Spring 支持定义多个全局异常处理器:

less 复制代码
@Order(1)
@RestControllerAdvice
class BizExceptionHandler {}

@Order(2)
@RestControllerAdvice
class SystemExceptionHandler {}

优先级越小,越先执行。


7. 异常处理的第一版架构形态

在"入门阶段",一个相对合理的异常架构通常是:

css 复制代码
Controller
   ↓
抛异常
   ↓
@ControllerAdvice
   ↓
统一错误响应

对应的返回结构示例:

css 复制代码
{
  "code": "SYSTEM_ERROR",
  "message": "系统异常,请稍后再试"
}

8. 异常处理流程图(概览)

php 复制代码
throw Exception
HTTP Request
DispatcherServlet
Controller
Exception Resolver
@ExceptionHandler / ControllerAdvice
HTTP Response

图1 Spring MVC 异常处理基本流程图


9. 本篇小结(从入门视角看异常)

到这里,我们只做了三件事:

  1. 纠正"异常只是 try-catch"的认知
  2. 明确异常是 Web 系统的统一出口
  3. 理解 Spring MVC 为什么要集中处理异常

但我们还没有回答几个关键问题:

  • 异常是如何一步步被解析的?
  • 为什么 @ExceptionHandler 能生效?
  • Spring Boot 的 /error 是干什么的?
  • 为什么有些异常进不了 ControllerAdvice?

👉 这些问题,都需要进入源码层面才能解释清楚。


参考资料


结束语

掘金点击访问Qiuner CSDN点击访问Qiuner GitHub点击访问Qiuner Gitee点击访问Qiuner

专栏 简介
📊 一图读懂系列 图文并茂,轻松理解复杂概念
📝 一文读懂系列 深入浅出,全面解析技术要点
🌟持续更新 保持学习,不断进步
🎯 人生经验 经验分享,共同成长

你好,我是Qiuner. 为帮助别人少走弯路而写博客

如果本篇文章帮到了你 不妨点个 吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。

代码都在Github或Gitee上,如有需要可以去上面自行下载。记得给我点星星哦😍

如果你遇到了问题,自己没法解决,可以去我掘金评论区问。CSDN评论区和私信消息看不完 掘金消息少一点.

上一篇推荐 链接
Java程序员快又扎实的学习路线 点击该处自动跳转查看哦
一文读懂 AI 点击该处自动跳转查看哦
一文读懂 服务器 点击该处自动跳转查看哦
2024年创作回顾 点击该处自动跳转查看哦
一文读懂 ESLint配置 点击该处自动跳转查看哦
老鸟如何追求快捷操作电脑 点击该处自动跳转查看哦
未来会写什么文章? 预告链接
一文读懂 XX? 点击该处自动跳转查看哦
2025年终总结 点击该处自动跳转查看哦
一图读懂 XX? 点击该处自动跳转查看哦
相关推荐
Han.miracle2 小时前
Java集合核心:ArrayList与LinkedList深度解析
java·开发语言
篱笆院的狗2 小时前
Group by很慢,如何定位?如何优化?
java·数据库
期待のcode2 小时前
Java的反射
java·开发语言
2201_757830872 小时前
AOP入门程序
java·开发语言
雨中飘荡的记忆2 小时前
MyBatis反射模块详解
java·mybatis
宸津-代码粉碎机2 小时前
Spring 6.0+Boot 3.0实战避坑全指南:5大类高频问题与解决方案(附代码示例)
java·数据仓库·hive·hadoop·python·技术文档编写
笃行客从不躺平2 小时前
ThreadLocal 复习一
java·开发语言
程序帝国2 小时前
SpringBoot整合RediSearch(完整,详细,连接池版本)
java·spring boot·redis·后端·redisearch