全局异常处理器GlobalExceptionHandler,处理项目中抛出的业务异常

张开发
2026/6/10 2:20:55 15 分钟阅读
全局异常处理器GlobalExceptionHandler,处理项目中抛出的业务异常
/** * 全局异常处理器处理项目中抛出的业务异常 */ RestControllerAdvice Slf4j public class GlobalExceptionHandler { /** * 捕获业务异常 * param ex * return */ ExceptionHandler public Result exceptionHandler(BaseException ex){ log.error(异常信息{}, ex.getMessage()); return Result.error(ex.getMessage()); } }RestControllerAdvice大喇叭通知者这个注解是ControllerAdvice和ResponseBody的合体。它的作用告诉 Spring 框架“嘿我是所有 Controller 的贴身管家。”全局性体现在哪只要你的项目中比如sky-server任何一个 Controller 抛出了异常这个管家都会第一时间收到消息并接管后续的处理。2.ExceptionHandler捕获网核心逻辑这个注解挂在方法上才是真正的“捕获”动作。它的逻辑方法参数里写的是BaseException ex这表示“只要程序里抛出的异常是BaseException或者它的子类都统统给我送到这个exceptionHandler方法里来受审”如果你不写这个异常会直接甩给前端页面会报丑陋的500 Internal Server Error或者一堆看不懂的堆栈信息。3. 为什么要有这个东西实战场景想象一下你在写“员工登录”逻辑你发现密码错了你直接在 Service 层throw new PasswordErrorException(密码错误);。这时候Service 停了Controller 也停了。奇迹发生了Spring 发现有异常瞬间瞬移到你的GlobalExceptionHandler。它执行Result.error(ex.getMessage())把“密码错误”包装成一个漂亮的 JSON 格式返回给前端。impl层写的//2、处理各种异常情况用户名不存在、密码不对、账号被锁定 if (employee null) { //账号不存在 throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND); } /** * 账号不存在异常 */ public class AccountNotFoundException extends BaseException { public AccountNotFoundException() { } public AccountNotFoundException(String msg) { super(msg); } } /** * 业务异常 */ public class BaseException extends RuntimeException { public BaseException() { } public BaseException(String msg) { super(msg); } } RestControllerAdvice Slf4j public class GlobalExceptionHandler { /** * 捕获业务异常 * param ex * return */ ExceptionHandler public Result exceptionHandler(BaseException ex){ log.error(异常信息{}, ex.getMessage()); return Result.error(ex.getMessage()); } }1. 异常是会“向上漂浮”的冒泡机制在 Java 的调用栈里方法的执行是层层嵌套的。执行顺序通常是Tomcat容器-Filter-Interceptor-Controller-Service-Mapper当你的ServiceImpl抛出异常时Service 说我不处理这个异常没写try-catch那我崩了。调用者Controller收到消息Controller 发现它调用的 Service 崩了如果 Controller 也不处理同样没写try-catch那Controller 也会崩掉。Spring 登场异常现在已经“漂”到了 Controller 这一层。由于 Controller 是被 Spring 管理的Spring 的DispatcherServlet会像雷达一样扫描到这个异常。这个过程发生在毫秒级。对你来说你只是throw了一下对前端来说他只是收到了一个 JSON。中间这一大串复杂的“寻人、匹配、反射调用、序列化”全是 Spring 帮你负重前行。所以为什么叫“全局拦截”因为无论你的项目里有多少个 Controller无论哪个 Controller 调了多少个 Service最后只要异常没被截住通通都会汇聚到 DispatcherServlet 这里然后被你的GlobalExceptionHandler统一收编。1. DispatcherServlet餐厅的大堂经理总调度它是整个 Spring MVC 的核心也是唯一的入口。它的职责客人前端请求进门后大堂经理负责接待。他并不亲自下厨不写业务逻辑但他知道该把客人带到哪桌匹配 Controller出事了该找谁处理。在异常流程中当前端请求在餐厅里出了意外报错了大堂经理会第一时间收到消息。他不会慌而是翻开他的**“应急预案手册”**。2. HandlerExceptionResolver应急预案手册接口这是一个接口Interface它是大堂经理手里那本手册的总称。它的职责定义了“怎么处理异常”的标准流程。它的逻辑手册里写着“如果餐厅发生意外必须有一个人出来解决并给客人一个说法返回一个视图或 JSON。”注意它只是个抽象的概念。实际上手册里有很多页每一页代表一种不同的处理方式。3. ExceptionHandlerExceptionResolver最牛的应急小组实现类这是HandlerExceptionResolver接口里最常用、最智能的一个实现类。它的职责专门负责寻找打上了ExceptionHandler注解的方法。为什么叫它“最牛”它是自动档。它会扫描你写的GlobalExceptionHandler类。它能精准匹配。如果你抛出的是BaseException它绝不会去调用处理NullPointerException的方法。它懂JSON。因为它配合RestControllerAdvice使用它知道把结果转成 JSON 给前端而不是甩一个 404 页面。 总结一下这三者是怎么配合的当你的ServiceImpl抛出了BaseException大堂经理 (DispatcherServlet)接到消息发现 Controller 崩了赶紧翻开应急手册 (HandlerExceptionResolver)。他发现手册里有一个最牛小组 (ExceptionHandlerExceptionResolver)专门管这事。这个小组迅速出动找到了你的GlobalExceptionHandler。小组发现你有一个方法标记了ExceptionHandler(BaseException.class)于是立即执行它。最后小组把结果Result.error(...)交给大堂经理大堂经理把这个结果体面地送给客人前端。步骤参与者动作1ServiceImpl发现不对throw new BaseException(报错了)。2Controller没写try-catch异常继续往上漂。3DispatcherServlet接到“烫手山芋”启动异常解析流程。4ExceptionHandlerResolver找到你的RestControllerAdvice并执行匹配方法。5HTTP Response前端收到你自定义的ResultJSON而不是 500 报错页面。

更多文章