SpringBoot 实现全局异常处理

为什么要使用全局异常处理?

  1. 减少冗余代码: 在不使用全局异常处理器的情况下,项目中各层可能会出现大量的try {…} catch {…} finally {…}代码块,这些代码块不仅冗余,还影响代码的可读性。全局异常处理器允许我们在一个独立的类中定义对所有控制器异常的处理机制,从而消除大部分try-catch块。
  2. 统一异常处理: 全局异常处理器可以将异常按阶段(如进入Controller前的异常和Service层异常)进行分类处理,提供统一的异常处理策略。
  3. 自定义异常处理: 对于自定义的异常,使用全局异常处理器可以更容易地进行捕获和处理,而不是在每个可能抛出异常的地方都写一遍处理逻辑。
  4. 客户端友好性: 直接抛出异常给客户端可能会导致客户端无法理解异常信息。全局异常处理器允许我们将异常转换为客户端可理解的格式(如JSON响应),提高用户体验。

总结:
全局异常处理器的使用可以显著提高Spring Boot项目的代码质量和可维护性,减少冗余代码,提高代码的可读性和可维护性,统一异常处理策略,并提高客户端体验。因此,在Spring Boot项目中,使用全局异常处理器是一个值得推荐的做法。

代码实战

首先我们需要在项目中创建一个全局异常处理器类,比如叫 GlobalExceptionHandler,在该类上面需要加上 @RestControllerAdvice 注解表示这是一个全局异常处理器,代码示例:

import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletRequest;
import java.util.Optional;/*** 全局异常处理器*/
@RestControllerAdvice
public class GlobalExceptionHandler {public final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 拦截参数验证异常*/@SneakyThrows@ExceptionHandler(value = MethodArgumentNotValidException.class)public AjaxResult validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {BindingResult bindingResult = ex.getBindingResult();FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());String exceptionStr = Optional.ofNullable(firstFieldError).map(FieldError::getDefaultMessage).orElse(StrUtil.EMPTY);logger.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);return AjaxResult.error(exceptionStr);}/*** 拦截业务异常* BusinessServiceException 是一个自定义异常,以你们项目中的自定义异常为准,如果没有可以不写*/@ExceptionHandler(value = {BusinessServiceException.class})public AjaxResult handleBusinessException(HttpServletRequest request, BusinessServiceException ex){String exceptionStr = ex.getMessage();logger.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);return AjaxResult.error(exceptionStr);}/*** 拦截未捕获异常*/@ExceptionHandler(Throwable.class)public AjaxResult defaultErrorHandler(HttpServletRequest request, Throwable throwable) {logger.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);return AjaxResult.error(throwable.getMessage());}private String getUrl(HttpServletRequest request) {if (StringUtils.isEmpty(request.getQueryString())) {return request.getRequestURL().toString();}return request.getRequestURL().toString() + "?" + request.getQueryString();}
}

到这里,全局异常处理器就已经写完了,接下来就是验证环节。我会依次验证每一个异常场景是否可以成功拦截。
在验证之前,我们先来看一张图:
image.png
上面这张图片大家应该都很熟悉,这就是在不对接口中的异常进行处理时 SpringBoot 默认返回的页面,如果我们的异常处理器生效了,那么返回的就应该是我们封装好的数据。

验证 MethodArgumentNotValidException 异常

在验证 MethodArgumentNotValidException 异常前,需要先说明一下该异常常见的触发场景:

  • 数据转换问题:当客户端传递的数据类型无法正确转换为方法参数的类型时,会发生数据转换问题,导致MethodArgumentNotValidException异常。这通常发生在使用@RequestBody注解处理JSON请求体时,如果JSON格式不正确或无法映射到目标对象,就会抛出此异常。
  • 错误的验证注解: 如果在实体类中对属性使用了不符合验证需求的注解,如@NotNull、@Size等,并且请求中的数据不符合这些注解指定的规则,那么在Spring MVC将请求参数解析为控制器方法参数时会触发校验,并抛出MethodArgumentNotValidException异常。
  • 未通过校验的参数: 当使用@Valid或@Validated注解对方法参数进行校验时,如果参数不符合校验规则(如非空、长度、格式等),会抛出MethodArgumentNotValidException异常。例如,一个表单提交到Controller时,如果表单中的某个字段不符合校验规则,则会抛出此异常。

接下来我们创建一个 UserEntity 类做为方法参数用来测试:

import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;@Data
public class UserEntity {@NotNull(message = "用户ID不能为空")private Long userId;@NotNull(message = "用户名不能为空")@Size(min = 2, max = 30, message = "用户名长度必须在2到30个字符之间")private String username;}
// 注意,这个方法我们并没有采取任何 try catch 操作
@PostMapping("/test")
public AjaxResult Test(HttpServletRequest request, @RequestBody @Valid UserEntity userEntity) {Map<String,Object> model = new HashMap<String,Object>();String planCode = request.getParameter("planCode");String planDefineId = request.getParameter("planDefineId");if (StringUtils.isBlank(planDefineId) || StringUtils.isBlank(planCode)) {throw new BusinessServiceException("参数不可以为空!");}// 获取信息String planName = retailProductCommon.getproductName(planCode, planDefineId);model.put("planName", planName);return AjaxResult.success(model);
}

这里我们使用 PostMan 工具去请求这个接口,并设置一个不满足要求的参数,看下是否会以为抛错导致程序中断。请求结果:
image.png
可以发现,正常返回错误信息给客户端了,并没有直接抛500。

验证自定义异常

验证 BusinessServiceException 异常也很简单,我们将上面 UserEntity 的参数补全,但是代码中为空会抛错的那个两个参数我们不传,看下会有什么结果。请求结果:
image.png
这里同样是我们自定义的返回对象,并不是SpringBoot默认的500页面,所以验证成功。

验证其他异常

接下来我们在 test 接口中手动写一个异常代码出来,比如int num = 1/0当代码执行到这里的时候会抛出 / by zero,我们看下结果如何,会不会被 defaultErrorHandler 方法拦截:
image.png
这里同样是我们自定义的返回对象,并不是SpringBoot默认的500页面,所以验证成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/30786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大润发超市购物卡怎么用?

收到大润发超市的礼品卡以后&#xff0c;我才发现&#xff0c;最近的大润发也得十来公里 为了100块的大润发打车也太不划算了 叫外送也不在配送范围内 最后没办法&#xff0c;在收卡云上出掉了&#xff0c;还好最近价格不错&#xff0c;也不亏&#xff0c;收卡云的到账速度也…

Windows反截屏开发实现

文章目录 Windows反截屏开发实现1. SetWindowDisplayAffinity2. 反截屏系统3. 总结 Windows反截屏开发实现 最近在我们云桌面中需要做到反截屏能力&#xff0c;所谓反截屏就是我们无法通过截图软件&#xff08;微信&#xff0c;QQ&#xff0c;截图等程序&#xff09;截取桌面的…

redis 缓存jwt令牌设置更新时间 BUG修复

大家好&#xff0c;今天我又又又来了&#xff0c;hhhhh。 上文中 我们永redis缓存了token 但是我们发现了 一个bug &#xff0c;redis中缓存的token 是单用户才能实现的。 就是 我 redis中存储的键 名 为token 值 是jwt令牌 &#xff0c;但是如果 用户a 登录 之后 创建一个…

FlinkCDC 3.1.0 与 Flink 1.18.0 安装及使用 Mysql To Doris 整库同步,使用 pipepline连接器

cd flink-cdc-3.1.0 bin/flink-cdc.sh 会用到 linux的系统环境变量&#xff08;vim /etc/profile配置&#xff09;&#xff0c;使用环境变量 FLINK_HOME flinkcdc & flink 安装及使用&#xff1a; 1、flink-cdc-3.1.0/lib/ 内容如下&#xff1a; 2、flink-cdc-3.1.0/mysql…

MS31211低压、大电流、单全桥驱动

MS31211 是一款低压、大电流、单全桥驱动。它 可应用于低电压及电池供电的运动控制场合&#xff0c;并且内 置电荷泵来提供内部功率 NMOS 所需的栅驱动电压。 MS31211 可以提供最高 3.2A 的峰值电流&#xff0c;其功 率电源供电范围从 1.8V 到 10V &#xff0c;逻…

JUC并发编程-第二天:线程高级部分

线程高级部分 线程不安全原子性可见性有序性&#xff08;指令重排&#xff09; 线程不安全 多线程下并发同时对共享数据进行读写&#xff0c;会造成数据混乱线程不安全 当多线程下并发访问临界资源时&#xff0c;如果破坏其原子性、可见性、有序性&#xff0c;可能会造成数据不…

JavaEE多线程(2)

文章目录 1..多线程的安全1.1出现多线程不安全的原因1.2解决多线程不安全的⽅法1.3三种典型死锁场景1.4如何避免死锁问题2.线程等待通知机制2.1等待通知的作用2.2等待通知的方法——wait2.3唤醒wait的方法——notify 1…多线程的安全 1.1出现多线程不安全的原因 线程在系统中…

前端练习小项目——视觉冲击卡片

前言&#xff1a; 前言&#xff1a;在学习完HTML和CSS之后&#xff0c;我们就可以开始做一些小项目了&#xff0c;本篇文章所讲的小项目为——视觉冲击卡片 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 先让我们看一下效果&a…

maxKb+ollama+lama2-chinese 基于知识库+本地模型的知识问答系统

maxKbollamalama2-chinese 基于知识库本地模型的知识问答系统 搭建步骤 搭建maxKb docker run -d --namemaxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data cr2.fit2cloud.com/1panel/maxkb# 用户名: admin # 密码: MaxKB123..github的访问地址&#xff1a;https://…

Vant2组件库的基础应用

目录 一、Picker 选择器 1.1、数组对象处理 1.2、每个选项颜色设置 二、滚动分页加载列表 三、Calendar 日历(可选范围限制) 四、input值过滤 官网&#xff1a;Vant 2 - Mobile UI Components built on Vue 一、Picker 选择器 官网示例数据&#xff1a; columns: [杭州…

计算机网络实验之单交换机互联终端实验

1.网线 4对&#xff0c;8根&#xff0c;RJ-45连接器&#xff08;水晶头&#xff09;&#xff1b; &#xff08;1&#xff09;直通线 双绞线缆两端按照EIA/TIA568B规格连接水晶头&#xff0c;该双绞线为直通线。 橘白1&#xff0c;橘2&#xff0c;绿白3&#xff0c;蓝4&#…

WPF学习(2)--类与类的继承2-在窗口的实现

一、代码分析 1.Animal.cs 1.1 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace AnimalNamespace {public class Animal{public string Name { get; set; }public int Age { get; set…

RabbitMQ的简单使用 —— Python篇

&#xff08;一&#xff09;RabbitMQ的简介 RabbitMq 是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理中间件。消息队列是一种应用程序对应用程序的通行方式&#xff0c;应用程序通过写消息&#xff0c;将消息传递于队列&#xff0c;由另一应用程序读取 完…

JavaWeb项目配置教程

将你的项目&#xff08;只有代码的文件&#xff0c;不是整个文件&#xff09;拖入idea 找到数据库配置代码&#xff08;一般在Util包里面&#xff0c;或者是properties配置文件&#xff09;并将密码修改为你的数据库密码。 点击Edit Configurations 点击Configure&#xff0…

SOLIDWORKS安装运行环境建议 慧德敏学

SOLIDWORKS是一款要求很高的软件。无可否认。您的电脑功能越强大&#xff0c;运行得越好&#xff0c;但是我们也要考虑购买成本&#xff0c;因此&#xff0c;选择正确的配置很重要。在选择用于SOLIDWORKS的电脑配置时&#xff0c;需要综合考虑多个方面以确保软件能够流畅、以更…

Hi3861 OpenHarmony嵌入式应用入门--PWM 三色灯

这篇文章是讲解的pwm控制三色灯的部分&#xff0c;这部分也是后续全彩智能灯的基础。 硬件原理如下 IO管脚定义在hi-12f_v1.1.2-规格书-20211202.pdf文档中 GPIO API API名称 说明 unsigned int IoTGpioInit(unsigned int id); GPIO模块初始化 hi_u32 hi_io_set_func(hi_i…

无引擎游戏开发(2):最简游戏框架 | EasyX制作井字棋小游戏I

一、EasyX中的坐标系 不同于数理中的坐标系&#xff0c;EasyX中的y轴是竖直向下的 二、渲染缓冲区 之前的程序添加了这三个函数改善了绘图时闪烁的情况: 小球在"画布“上移动的过程就是我们在调用绘图函数&#xff0c;这个”画布“就是渲染缓冲区&#xff0c;先绘制的内…

【配置】Notion自动化备份到github方案

步骤 打开notion网页&#xff0c;获取到需要的值 token_v2 找到请求getSpaces的 Cookie 值 token_v2 space_id 找到请求getSpaces的响应结果space,如下图&#xff1a; file_token 找个页面点击导出&#xff0c;之后拿到这个配置项 注意&#xff1a;配置项会过期&#xff0c…

基于一种改进熵方法的旋转机械故障诊断模型(MATLAB)

熵的概念起源于热力学&#xff0c;1884年&#xff0c;玻尔兹曼定义熵&#xff0c;用以描述分子热运动的无序性和混乱度。1948年&#xff0c;Shannon在其发表的《AMathematicalTheoryofCommunication》中提出香农熵&#xff0c;首次将“熵”引入信息度量范畴&#xff0c;为信息论…

RK3568技术笔记十三 Ubuntu的编译

Ubuntu文件系统编译 在编译前需要按照前面的方法初始化编译环境&#xff0c;否则会导致编译失败&#xff08;若配置过则无需重复配置&#xff09;。 按下述方法编译的Ubuntu系统&#xff0c;用户名是&#xff1a;dianyu 密码&#xff1a;1 编译Ubuntu&#xff0c;执…