首页 > 其他分享 >全局异常捕获(@RestControllerAdvice)介绍和使用

全局异常捕获(@RestControllerAdvice)介绍和使用

时间:2024-03-19 09:01:10浏览次数:25  
标签:return 捕获 public error import RestControllerAdvice 异常 全局

@RestControllerAdvice是什么

@RestControllerAdvice是Spring框架提供的一个注解,用于定义全局异常处理器和全局数据绑定设置。它结合了@ControllerAdvice和@ResponseBody两个注解的功能。

@ControllerAdvice

@ControllerAdvice是一个用于定义全局控制器增强(即全局异常处理和全局数据绑定)的注解。通过使用@ControllerAdvice,我们可以将异常处理和数据绑定逻辑集中到一个类中,避免在每个控制器中重复编写相同的异常处理代码。

@ResponseBody

@ResponseBody是用于指示控制器方法返回的对象将被直接写入响应体中的注解。它告诉Spring将方法的返回值序列化为JSON或其他适当的响应格式,并将其作为HTTP响应的主体返回给客户端。

@RestControllerAdvice作用

当我们在类上使用@RestControllerAdvice注解时,它相当于同时使用了@ControllerAdvice和@ResponseBody。这意味着被@RestControllerAdvice注解标记的类将被视为全局异常处理器,并且异常处理方法的返回值将以JSON格式直接写入响应体中。

通过在@RestControllerAdvice类中定义异常处理方法,我们可以捕获和处理控制器中抛出的异常,提供自定义的异常处理逻辑,以及返回适当的响应给客户端。这样可以统一处理应用程序中的异常情况,提高代码的可维护性和可读性。

常见可被捕获的异常类型

下面是常见可以被@RestControllerAdvice捕获的异常类型

  1. Exception:普通的Java异常,是大多数其他异常的基类。
  2. RuntimeException:运行时异常,包括NullPointerException、IllegalArgumentException等。
  3. HTTP状态码异常:例如,HttpStatus.NOT_FOUND表示资源未找到,HttpStatus.BAD_REQUEST表示请求错误等。
  4. ValidationException:数据验证异常,例如使用JSR-303或Hibernate Validator进行的数据验证失败。
  5. MethodArgumentNotValidException:请求方法参数验证失败时抛出的异常。
  6. HttpMessageNotReadableException:当请求的HTTP消息无法读取或解析时抛出的异常,例如请求体格式错误。
  7. HttpRequestMethodNotSupportedException:当请求的HTTP方法不受支持时抛出的异常。
  8. MissingServletRequestParameterException:当请求缺少必需的参数时抛出的异常。
  9. BindException:数据绑定失败时抛出的异常,通常与表单提交相关。
  10. ConstraintViolationException:当使用Bean验证(如Hibernate Validator)时,违反约束条件时抛出的异常。
  11. NoHandlerFoundException:当找不到适合处理当前请求的处理程序时抛出的异常。
  12. 自定义的异常类

项目中实战

项目依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

整体项目结构

img

全局返回实体类R

全局返回实体类

import lombok.Data;

import java.io.Serializable;

@Data
public class R implements Serializable {

    private Integer code;
    private String message;
    private Object data;
    private static final Integer DEFAULT_SUCCESS_CODE = 200;
    private static final Integer DEFAULT_ERROR_CODE = 500;

    /**
     * 成功
     *
     * @return
     */
    public static R success() {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage("请求成功");
        r.setData(new Object());
        return r;
    }

    /**
     * 成功-data
     *
     * @return
     */
    public static R success(Object data) {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage("请求成功");
        r.setData(data);
        return r;
    }

    /**
     * 成功-data和message
     *
     * @return
     */
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage(message);
        r.setData(data);
        return r;
    }

    /**
     * 错误
     *
     * @return
     */
    public static R error() {
        R r = new R();
        r.setCode(DEFAULT_ERROR_CODE);
        r.setMessage("请求失败");
        return r;
    }

    /**
     * 错误-code
     *
     * @return
     */
    public static R error(Integer code) {
        R r = new R();
        r.setCode(code);
        r.setMessage("请求失败");
        return r;
    }

    /**
     * 错误-message
     *
     * @return
     */
    public static R error(String message) {
        R r = new R();
        r.setCode(DEFAULT_ERROR_CODE);
        r.setMessage(message);
        return r;
    }

    /**
     * 错误-code和message
     *
     * @return
     */
    public static R error(Integer code, String message) {
        R r = new R();
        r.setCode(code);
        r.setMessage(message);
        r.setData(new Object());
        return r;
    }
}

自定义异常CustomException类

有些场景需要抛出自定义异常,就可以写一个继承RuntimeException类的异常

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author csn
 * @date 2024/2/27
 */

@EqualsAndHashCode(callSuper = true)
@Data
public class CustomException extends RuntimeException{

    public CustomException(String message) {
        super(message);
    }
}

GlobalExceptionHandler类

捕获对应的异常类型,并给出相应的提示

import com.example.test.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * RestControllerAdvice = ControllerAdvice + ResponseBody
 * 是controller的增强器,可以全局捕获spring mvc抛的异常
 * 优雅异常处理程序
 * 统一拦截处理
 * 在该类中,可以定义多个方法,不同的方法处理不同的异常
 */
@RestControllerAdvice(annotations = RestController.class)
@Slf4j
public class GlobalExceptionHandler {


    /**
     * 处理Validated验证异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BindException.class})
    public R bindExceptionHandler(BindException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        log.error("BindException:", e);
        return R.error(objectError.getDefaultMessage());
    }

    /**
     * 处理单个文件超大异常
     *
     * @param e
     * @return
     * @ExceptionHandler 注解用来指明异常的处理类型
     */
    @ExceptionHandler(FileSizeLimitExceededException.class)
    public R fileSizeLimitExceededExceptionHandler(FileSizeLimitExceededException e) {
        log.error("FileSizeLimitExceededException异常:", e);
        return R.error("单个文件大小不允许超过2MB");
    }

    /**
     * 处理请求数据超大异常
     *
     * @param e
     * @return
     * @ExceptionHandler
     */
    @ExceptionHandler(SizeLimitExceededException.class)
    public R sizeLimitExceededExceptionHandler(SizeLimitExceededException e) {
        log.error("SizeLimitExceededException异常:", e);
        return R.error("请求数据大小不允许超过10MB");
    }

    /**
     * 处理NullPointerException异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({NullPointerException.class})
    public R customExceptionHandler(NullPointerException e) {
        log.error("NullPointerException异常:", e);
        return R.error("空指针异常");
    }

    /**
     * 处理CustomExceptionHandler异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({CustomException.class})
    public R customExceptionHandler(CustomException e) {
        log.error("CustomException异常:", e);
        return R.error(e.getMessage());
    }

    /**
     * 处理其它异常:统一捕获,写入异常日志
     * 例1:HttpRequestMethodNotSupportedException 路由请求方式异常
     * 例2:NullPointerException 空指针异常
     * 例3:BadSqlGrammarException sql异常
     * ......
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public R exceptionHandler(Exception e) {
        log.error("Exception异常:", e);
        return R.error();
    }
}

TestController类

在TestController类中写三个接口

第一个接口:抛出1/0异常

第二个接口:抛出自定义异常

第二个接口:抛出空指针异常

import com.example.test.exception.CustomException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author csn
 * @date 2024/3/18
 */
@RestController
public class TestController {

    @GetMapping("/test1")
    public String testError1() {
        int i = 1/0;
        return "test1";
    }

    @GetMapping("/test2")
    public String testError2() {
        String a = "aa";
        if (!a.equals("a")){
            throw new CustomException("自定义异常");
        }
        return "test2";
    }

    @GetMapping("/test3")
    public String testError3() {
        String a = null;
        if (a.equals("a")){
        }
        return "test3";
    }

}

调用接口报错提示

  1. test1接口:

img

  1. test2接口:

img

  1. test3接口

img

注意事项

  1. @RestControllerAdvice并不能捕获所有异常,例如Error类的子类(如OutOfMemoryError)通常无法被捕获。
  2. 某些异常可能会被其他全局异常处理器或框架层面的异常处理机制捕获,而不会被@RestControllerAdvice处理。
  3. Error及其子类:Error是Throwable的子类,表示严重的错误,通常由虚拟机抛出,如OutOfMemoryError、StackOverflowError等。这些异常通常意味着应用程序处于不可恢复的状态,因此无法被@RestControllerAdvice捕获。
  4. ThreadDeath:ThreadDeath是Error的子类,它表示线程意外终止的异常。与其他Error一样,ThreadDeath异常也无法被@RestControllerAdvice捕获。
  5. VirtualMachineError及其子类:VirtualMachineError是Error的子类,表示与Java虚拟机相关的错误,如InternalError、UnknownError等。这些错误通常与虚拟机的内部状态或配置有关,无法被@RestControllerAdvice捕获。

标签:return,捕获,public,error,import,RestControllerAdvice,异常,全局
From: https://blog.csdn.net/c103363/article/details/136812854

相关文章

  • 基于似然场的全局定位
    似然场法定位检测似然场最小二乘问题构建机器人Robot在地图World中的位姿表示为\(\boldsymbol{x}\),激光雷达扫描得到的点云表示为\(\{p_i^R\}\),其中\(^R\)表示在机器人坐标系下的坐标,\(_i\)表示点云中第i个点。\[\boldsymbol{x}=[x,y,\theta]^{\rm{T}}\]那么,点云中机器人......
  • Eplan插件 - 修改全局栅格
    前言在工作中,经常使用到窗口宏,尤其是在驱动器比较多的时候,可能一连几十页都是伺服驱动器,但是由于窗口宏是从其他地方获取而来。而窗口宏的制作者使用了过大或过小的栅格就会出现画图连接不齐的情况,那么就需要手动修改栅格的大小。在Eplan中默认修改的是当前页面的栅格。在页数很......
  • 配置全局变量直接调用函数
    配置全局变量直接调用函数目的想要在项目中使用defs.utills.getFunction()的方式,直接调用我们的函数,不再使用import的方式进行导入,直接在工程化上做手脚进行自动导入解决。过程首先我们在我们的项目框架src/目录下定义一个全局的utill文件,这个我们可以分类型创建文件夹、......
  • python--异常捕获+类的创建+类属性
    异常处理写法一try:可能会报错的代码print(‘不报错执行’)except:print(‘报错的时候执行’)写法二try:#可能报错的代码print(‘不报错执行1’)except:print(‘报错的时候执行’)else:print(‘不报错执行2’)写法三try:#可能报错的代码print(‘不......
  • Linux第79步_使用自旋锁保护某个全局变量来实现“互斥访问”共享资源
    自旋锁使用注意事项:自旋锁保护的“临界区”要尽可能的短。因此,在open()函数中申请“spinlock_t自旋锁结构变量”,然后在release()函数中释放“spinlock_t自旋锁结构变量”,这种方法就行不通了。如果使用一个变量“dev_stats”来表示“共享资源的使用标志”,则“dev_stats>0”,......
  • 【WPF】设置全局样式
    目的:创建一个资源字典,然后自动引入到各个Window或UserControl中,可以随意使用。方式:创建Style文件夹在Style文件夹内创建一个ButtonStyleStyle.xaml的资源字典示例如下:<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"......
  • 变更数据捕获(CDC)工具综述
    变更数据捕获(ChangeDataCapture,CDC)是一种技术,用于实时捕获数据库中的变更(如插入、更新和删除操作),并将这些变更同步到其他数据库或数据仓库中,从而保持数据环境的一致性和实时性。CDC技术对于实现实时数据分析、数据集成、数据复制和备份等场景至关重要。本文将介绍当前市场上一......
  • vue 3+TS 封装自定义右键全局菜单(虚拟节点)
    有时我们需要点击(右键或左键)某个元素时弹出菜单,实现复制、粘贴、删除等功能。本文将介绍如何封装一个自定义的右键全局菜单(无三方依赖)。封装的菜单可自定义菜单项,图标,禁用,分割线,隐藏等。并且可以在全局任意地方使用。源码在文章末尾。效果使用<template><div>......
  • Vue学习笔记52--全局事件总线
    Vue全局事件总线:一种组件之间通信的方式,适用于任意组件之间通信。1.所有组件,即VueComponent所有的组件实例对象vc2.每次使用VueComponent都是new一个新的vc3.Vue.prototype=VueComponent.prototype.__proto__(可以让组件实例对象vc访问到Vue原型上的属性、方法)4.$emit、$o......
  • js如何拦截全局Fetch的请求和响应
    目前,团队采用了根路径转发的方式,将接口请求转发到服务器上,实现了一定的解耦。然而,随着团队后端策略的变化,现在希望前端直接请求一个新的接口域名,而不再经过中间层的处理。在这种情况下,由于之前的代码中没有对接口请求进行统一的封装,需要考虑如何以最小的成本进行迁移。Fetc......