SpringBoot 实现全局异常处理

news2024/12/21 19:02:22

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

  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.coloradmin.cn/o/1843486.html

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

相关文章

第二十五篇——信息加密:韦小宝说谎的秘诀

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 加密这件事&#xff0c;对于这个时代的我们来说非常重要&#xff0c;那么…

数据分析中的数学:从基础到应用20240617

数据分析中的数学&#xff1a;从基础到应用 数据分析离不开数学的支持&#xff0c;统计学和概率论是其重要组成部分。本文将通过几个具体的实例&#xff0c;详细讲解数据分析中常用的数学知识&#xff0c;并通过Python代码演示如何应用这些知识。 1. 描述性统计 基本概念和用…

java学习--集合(大写二.2)

看尚硅谷视频做的笔记 2.collection接口及方法 jdk8里有一些默认的方法&#xff0c;更多的是体现的是一种规范&#xff0c;规范更多关注的是一些抽象方法。 看接口里面的抽象方法&#xff0c;选一个具体的实现类。 测试collection的方法&#xff0c;存储一个一个数据都有哪些…

ENVI实战—一文搞定监督分类

实验1&#xff1a;利用ROI建立样本训练集和验证集 目的&#xff1a;学会利用ROI建立计算机分类时的样本集 过程&#xff1a; ①导入影像&#xff1a;打开ENVI&#xff0c;选择“打开→打开为→光学传感器→ESA→Sentinel-2”&#xff0c;将Sentinel-2影像导入到ENVI平台中。…

LabVIEW与3D相机开发高精度表面检测系统

使用LabVIEW与3D相机开发一个高精度表面检测系统。该系统能够实时获取三维图像&#xff0c;进行精细的表面分析&#xff0c;广泛应用于工业质量控制、自动化检测和科学研究等领域。通过真实案例&#xff0c;展示开发过程中的关键步骤、挑战及解决方案&#xff0c;确保系统的高性…

MySQL客户端与服务端建立连接抓包分析

文章目录 MySQL客户端与服务端建立连接流程抓包分析1.连接建立流程2.各类数据包介绍2.1挑战数据包2.2认证数据包2.3切换认证插件请求数据包2.4切换认证插件响应数据包2.5成功数据包2.6失败数据包3.注意点4.测试代码MySQL客户端与服务端建立连接流程抓包分析 抓包工具采用的是W…

大厂的 404 页面都长啥样?看到最后一个,我笑了~

每天浏览各大网站&#xff0c;难免会碰到404页面啊。你注意过404页面么&#xff1f;猿妹搜罗来了下面这些知名网站的404页面&#xff0c;以供大家欣赏&#xff0c;看看哪个网站更有创意&#xff1a; 腾讯 网易 淘宝 百度 新浪微博 新浪 京东 优酷 腾讯视频 搜狐 携程 去哪儿 今…

C#——装箱与拆箱详情

装箱与拆箱 装箱: 将值类型转换成引用类型的过程&#xff1b; 拆箱: 把引用类型转为值类型的过程&#xff0c;就是拆箱 装箱 拆箱

usb摄像头应用编程

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

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

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

账号和权限的管理

文章目录 管理用户账号和组账号用户账号的分类超级用户普通用户程序用户 UID&#xff08;用户id)和(组账号)GIDUID用户识别号GID组标识号 用户账号文件添加用户账号设置/更改用户口令 管理用户账号和组账号 用户账号的分类 超级用户 root 用户是 Linux 操作系统中默认的超级…

《米小圈动画汉字》汉字教育动画化:传统与创新的完美融合!

汉字&#xff0c;作为中华文化的瑰宝&#xff0c;承载着千百年来中华民族的智慧和思想。每一个汉字不仅仅是一个符号&#xff0c;更是一段历史的见证&#xff0c;一种文化的传承。在当今全球化的背景下&#xff0c;汉字教育面临着新的挑战与机遇。在这种背景下&#xff0c;如何…

LabVIEW程序退出后线程仍在运行问题

LabVIEW程序退出后&#xff0c;线程仍在运行的问题可能源于资源管理不当、未正确终止循环、事件结构未处理、并发编程错误以及外部库调用未结束等方面。本文将从这些角度详细分析&#xff0c;探讨可能的原因和解决方案&#xff0c;并提供预防措施&#xff0c;帮助开发者避免类似…

FPGA PCIe加载提速方案

目录 1.bit流压缩 2.flash加载速度 3.Tandem模式 1.bit流压缩 set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] 2.flash加载速度 打开bitstream setting&#xff0c;设置SPI的线宽和速率&#xff08;线宽按原理图设置&#xff0c;速率尽可能高&#xff09…

redis-基础篇(2)

黑马redis-基础篇笔记 3. redis的java客户端-Jedis 在Redis官网中提供了各种语言的客户端&#xff0c;地址&#xff1a;https://redis.io/docs/clients/ 标记为❤的就是推荐使用的java客户端&#xff0c;包括&#xff1a; Jedis和Lettuce&#xff1a;这两个主要是提供了Redi…

Vant2组件库的基础应用

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

有趣的 Oracle JDBC 驱动包命名问题 - ojdbc6 和 ojdbc14 哪个新?!

有趣的 Oracle JDBC 驱动包命名问题 - ojdbc6 和 ojdbc14 哪个新?! 1 背景概述 最近协助一个小兄弟排查了某作业使用 sqoop 采集 oracle 数据的失败问题&#xff0c;问题现象&#xff0c;问题原因和解决方法都挺直观&#xff0c;但在此过程中发现了一个有趣的 Oracle JDBC 驱…

JAVA每日作业day6.20

ok了家人们&#xff0c;今天学习了面向对象的继承&#xff0c;话不多说让我们看看怎么个事。 我们先把昨天学 面向对象-封装 的温习一下&#xff0c;来个例子 1&#xff0c;综合案例 做一个关于学生的随机点名器 定义了两个变量&#xff0c;name和age&#xff0c;给他们封装一…

java封装模块为jar包- FUSE 智能BPM低代码平台-企业级

第一种方法 将打好的项目模块jar放入lib文件夹内 操作后 项目便有了此jar包的依赖 第二种方法 使用 maven引入 可以参考我之前发的博客 IDEA导入jar包_idea导入jar包方法-CSDN博客

简单分享github

一、官网 GitHub: Let’s build from here GitHub 二、注册 通过简单的注册步骤&#xff0c;你就可以拥有一个属于自己的GitHub账号。再简单注册完成之后会需要验证你所输入的邮箱才能正常使用你的GitHub。 三、设置自己的库 在注册完成之后&#xff0c;完成一些简单的设置之…