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; } }
运行结果 

t1的运行结果:

t2的运行结果:

t3的运行结果:

问题

我们知道,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. 便于异常监控和日志记录
        集中监控:通过统一异常处理,可以更容易地实现对异常情况的集中监控和统计,为后续的故障排查和性能优化提供依据。
        详细日志:在异常处理过程中,可以记录详细的异常信息和上下文信息,便于后续的日志分析和问题定位。

Read more

从小项目到大型鸿蒙 App 的架构变化

从小项目到大型鸿蒙 App 的架构变化

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk
MySQL 数据类型核心指南:选型、实战与避坑

MySQL 数据类型核心指南:选型、实战与避坑

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 一. MySQL 数据类型分类总览 * 二. 数值类型:精准匹配数字范围与精度 * 2.1 整数类型(BIT/TINYINT/INT/BIGINT) * 2.1.1 TINYINT 类型测试 * 2.1.2 BIT 类型测试 * 2.1.3 INT/BIGINT 对比测试 * 2.2 小数类型(FLOAT/DOUBLE/DECIMAL) * 2.2.

By Ne0inhk
Flutter 三方库 flad_cli 的鸿蒙化适配指南 - 实现 Dart 工程的自适应模板扫描与脚手架自动化、支持端侧资源一键生成与代码架构规约校验实战

Flutter 三方库 flad_cli 的鸿蒙化适配指南 - 实现 Dart 工程的自适应模板扫描与脚手架自动化、支持端侧资源一键生成与代码架构规约校验实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 flad_cli 的鸿蒙化适配指南 - 实现 Dart 工程的自适应模板扫描与脚手架自动化、支持端侧资源一键生成与代码架构规约校验实战 前言 在进行 Flutter for OpenHarmony 的企业级项目矩阵开发时,如何保证上百个模块的目录结构、基础依赖、甚至是 import 规约保持高度一致?手动复制粘贴模板显然不可持续。flad_cli 是一个专为 Dart 项目设计的极简脚手架(Scaffold)命令行工具。它能根据预设规则自动生成或扫描工程文件。本文将探讨如何在鸿蒙端利用此工具构建极致的工业化开发流水线。 一、原直观解析 / 概念介绍 1.1 基础原理 flad_cli 建立在“代码生成(Code Gen)”与“扫描(

By Ne0inhk