1. 为什么异常值得单独写一整个系列
在多数业务代码中,异常往往被当作一种“不得不写的语法”:
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 体系结构
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 的三个常见问题
- 吞异常,导致问题被掩盖
- 重复代码,controller 层异常处理泛滥
- 破坏事务回滚逻辑
例如:
@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 中,异常被统一捕获:
try {
// 查找 handler 并执行
} catch (exception ex) {
dispatchexception = ex;
}
这意味着:
- controller 不需要感知异常如何返回
- 框架会统一接管异常
5. spring mvc 的三种基础异常处理方式
5.1 直接抛出异常(推荐)
@getmapping("/order")
public order getorder() {
throw new illegalargumentexception("参数错误");
}
异常会交给框架处理,而不是在 controller 内部解决。
5.2 @exceptionhandler:局部异常处理
@restcontroller
public class ordercontroller {
@exceptionhandler(illegalargumentexception.class)
public string handleillegalarg(exception e) {
return e.getmessage();
}
}
特点:
- 只对当前 controller 生效
- 适合非常局部的异常场景
5.3 @controlleradvice:全局异常处理(重点)
@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 本质上解决了三个问题:
- 异常集中管理
- 返回格式统一
- 与业务逻辑解耦
6.2 多个 controlleradvice 的顺序问题
spring 支持定义多个全局异常处理器:
@order(1)
@restcontrolleradvice
class bizexceptionhandler {}
@order(2)
@restcontrolleradvice
class systemexceptionhandler {}
优先级越小,越先执行。
7. 异常处理的第一版架构形态
在“入门阶段”,一个相对合理的异常架构通常是:
controller ↓ 抛异常 ↓ @controlleradvice ↓ 统一错误响应
对应的返回结构示例:
{
"code": "system_error",
"message": "系统异常,请稍后再试"
}
8. 异常处理流程图(概览)
图1 spring mvc 异常处理基本流程图
9. 本篇小结(从入门视角看异常)
到这里,我们只做了三件事:
- 纠正“异常只是 try-catch”的认知
- 明确异常是 web 系统的统一出口
- 理解 spring mvc 为什么要集中处理异常
但我们还没有回答几个关键问题:
- 异常是如何一步步被解析的?
- 为什么 @exceptionhandler 能生效?
- spring boot 的 /error 是干什么的?
- 为什么有些异常进不了 controlleradvice?
👉 这些问题,都需要进入源码层面才能解释清楚。
到此这篇关于spring boot异常处理try-catch应该怎么使用?的文章就介绍到这了,更多相关spring boot异常处理try-catch内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
参考资料
- spring framework reference – exception handling
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html - spring mvc 源码(dispatcherservlet)
https://github.com/spring-projects/spring-framework
发表评论