跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

SpringBoot 统一异常处理

综述由AI生成SpringBoot 使用 @ControllerAdvice 和 @ExceptionHandler 实现统一异常处理。文章提供了代码示例,涵盖异常处理类、结果封装类及测试控制器。通过调试源码分析 ExceptionHandlerMethodResolver,解释了子类异常处理器优先于父类的匹配机制。统一异常处理具有提升用户体验、解耦业务逻辑、减少冗余代码、规范风格及便于监控日志等优点。

技术博主发布于 2026/3/30更新于 2026/5/2327 浏览
SpringBoot 统一异常处理

SpringBoot 统一异常处理

统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的。@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

代码示例

ExceptionAdvice

接口返回为数据时,需要加 @ResponseBody 注解!!!

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import springbook.model.Result;

@Slf4j
@ResponseBody
@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler
    public Result handlerException(Exception e) {
        log.error("发生异常,e: {}", e);
        return Result.fail("内部错误");
    }

    @ExceptionHandler
    public Result handlerException(NullPointerException e) {
        log.error("发生异常,e:", e);
        return Result.fail("发生空指针异常");
    }

    @ExceptionHandler
    public Result handlerException(ArithmeticException e) {
        log.error("发生异常,e:", e);
        return Result.fail("发生算术异常");
    }
}

Result

import lombok.Data;
import springbook.enums.ResultStatus;

@Data
public class Result<T> {
    private ResultStatus code;
    private String errMsg; // 错误信息,如果业务成功,errMsg 为空
    private T data;

    public static <T> Result success(T data) {
        Result result = new Result<>();
        result.setCode(ResultStatus.SUCCESS);
        result.setData(data);
        return result;
    }

    public static Result fail(String msg) {
        Result result = new Result<>();
        result.setCode(ResultStatus.FAIL);
        result.setErrMsg(msg);
        return result;
    }

    public static Result fail(ResultStatus resultStatus, String msg) {
        Result result = new Result<>();
        result.setCode(resultStatus);
        result.setErrMsg(msg);
        return result;
    }
}

TestController

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1() {
        int result = 10 / 0;
        return "string";
    }

    @RequestMapping("/t2")
    public Integer t2() {
        String str = null;
        System.out.println(str.length());
        return 1;
    }

    @RequestMapping("/t3")
    public Boolean t3() {
        Integer[] integers = new Integer[]{1, 2, 3, 4};
        System.out.println(integers[5]);
        return true;
    }
}

问题

我们知道,ArithmeticException 和 NullPointerException 都属于 RuntimeException,同时 RuntimeException 又属于 Exception。

那么为什么同时有处理 ArithmeticException 和 Exception 的方法时,针对 ArithmeticException 异常,会调用处理 ArithmeticException 异常的方法而不是调用处理 Exception 异常的方法?

那么为什么同时有处理 NullPointerException 和 Exception 的方法时,针对 NullPointerException 异常,会调用处理 NullPointerException 异常的方法而不是调用处理 Exception 异常的方法?

结合源码了解问题源头

下面通过 Debug 的方式,结合源码来剖析为什么会出现上面的问题。

以上面代码的访问 test/t1 路径下方法导致 ArithmeticException 异常为例:

经过 Debug,我了解到,处理异常会走到下图所示的 ExceptionHandlerMethodResolver 类里面的 getMapperMethod()方法:

在刚进入该方法的时候,经过第一个黄色框内代码的处理后,会把当前具有处理异常的方法结合当前发生的异常进行匹配,最终匹配到两个合适类型的异常,分别是 Exception 异常和 ArithmeticException 异常:

但是在经过第二个黄色框内的代码处理后,会对刚刚匹配到的异常进行排序,根据异常的深度进行排序,并返回深度最浅的异常(即返回最合适的异常)。

经过第二个黄色框内的代码处理后,matches 里面的内容顺序发生变动:

此时,我们就知道了为什么同时有处理 ArithmeticException 和 Exception 的方法时,针对 ArithmeticException 异常,会调用处理 ArithmeticException 异常的方法而不是调用处理 Exception 异常的方法;以及为什么同时有处理 NullPointerException 和 Exception 的方法时,针对 NullPointerException 异常,会调用处理 NullPointerException 异常的方法而不是调用处理 Exception 异常的方法。

优点

  1. 提高用户体验 友好的错误信息:通过统一异常处理,可以为前端或其他服务提供一致的、易于理解的错误信息,而不是直接暴露后端的堆栈跟踪或技术细节,从而提升用户体验。 避免敏感信息泄露:统一处理可以避免在异常响应中不小心泄露敏感信息,如数据库表结构、内部实现细节等。
  2. 业务逻辑和异常处理逻辑解耦 代码结构清晰:将异常处理逻辑从业务逻辑中分离出来,使得业务代码更加专注于业务逻辑的实现,而异常处理代码则专注于异常的捕获、处理和响应。 易于维护:当需要修改异常处理逻辑时,只需在统一异常处理类中修改,而无需在多个业务代码中逐一修改,降低了维护成本。
  3. 减少冗余代码 避免重复编写:在传统的 Java 开发中,异常处理通常是分散在代码的各个部分中的。而使用统一异常处理可以避免在每个可能抛出异常的地方编写重复的异常处理代码。 提高代码复用性:通过定义全局的异常处理类,可以实现对多种异常的统一处理,提高代码的复用性。
  4. 便于代码风格统一 风格一致:统一异常处理有助于保持代码风格的一致性,使得整个项目的代码更加规范、整洁。 提升可读性:对于其他开发者来说,阅读和理解统一异常处理的代码也更为容易。
  5. 便于异常监控和日志记录 集中监控:通过统一异常处理,可以更容易地实现对异常情况的集中监控和统计,为后续的故障排查和性能优化提供依据。 详细日志:在异常处理过程中,可以记录详细的异常信息和上下文信息,便于后续的日志分析和问题定位。

目录

  1. SpringBoot 统一异常处理
  2. 代码示例
  3. ExceptionAdvice
  4. Result
  5. TestController
  6. 问题
  7. 结合源码了解问题源头
  8. 优点
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 低代码选型指南:AI 与低代码双向赋能企业数字化
  • Kubernetes Informer Indexer 索引机制深度解析
  • RocketMQ 服务启动与关闭指南
  • Nitter 私有部署指南:搭建无广告 Twitter 前端
  • 递归算法实战:汉诺塔与合并有序链表详解
  • Libvio 影视资源爬虫技术解析
  • 2026 低代码选型指南:AI 与低代码双向赋能
  • VSCode 接入智谱 GLM-4 及自定义大模型配置指南
  • GitHub Copilot Pro 使用指南:模型选择与配额管理策略
  • Levenberg-Marquardt 非线性最小二乘优化算法 C++ 实战实现
  • 宇树开源 UnifoLM-VLA-0 模型实现具身智能通用操作
  • CSS 元素显示模式详解:块级、行内与行内块
  • OpenClaw Web Search 工具使用指南
  • JDK 17 下载、安装与环境变量配置指南
  • Spatial Joy 2025 AR&AI 全球开发大赛参赛指南与资源解析
  • Word 文档的 5 种视图详解:页面、阅读、Web 版式、大纲及草稿
  • 5个Python Excel自动化技巧:告别繁琐数据处理
  • Microsoft Word 的 5 种视图详解:页面、阅读、Web 版式、大纲与草稿
  • Python 异步编程与协程实战指南
  • LeetCode Hot100:除自身以外数组的乘积

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online