SpringBoot中@RestControllerAdvice注解实现全局异常处理类

 更新时间:2024年01月17日 08:49:17   作者:吃青椒的秋草鹦鹉  
这篇文章主要介绍了SpringBoot中@RestControllerAdvice注解全局异常处理类,springboot中使用@RestControllerAdvice注解,完成优雅的全局异常处理类,可以针对所有异常类型先进行通用处理后再对特定异常类型进行不同的处理操作,需要的朋友可以参考下

需求

springboot中使用@RestControllerAdvice注解,完成优雅的全局异常处理类,可以针对所有异常类型先进行通用处理后再对特定异常类型进行不同的处理操作。

注解讲解

@RestControllerAdvice注解

@RestControllerAdvice是一个用于定义全局异常处理器的注解。当应用程序内发生未捕获的异常时,全局异常处理器将捕获该异常并返回对应的响应,以避免应用程序崩溃。它可以处理所有控制器中抛出的异常,包括请求处理方法中的异常、控制器构造函数中的异常等。

@RestControllerAdvice注解是@ControllerAdvice和@ResponseBody注解的组合,它的作用是将所有的异常处理结果都以JSON格式返回给客户端。

具体来说,当控制器中发生异常时,SpringBoot会在全局异常处理器中查找与异常匹配的处理方法,并执行该方法来处理异常。

处理方法可以返回任何类型的值,如果返回对象是DataVO类型,则会将其转换为JSON格式并返回给客户端。

如果返回值是String类型,则会将其解释为视图名称,并使用视图解析器来解析视图并生成HTML响应。

因此,使用@RestControllerAdvice注解可以方便地定义全局异常处理器,并将所有异常处理结果以JSON格式返回给客户端。

@ExceptionHandler注解

@ExceptionHandler是一个注解,用于定义异常处理方法。

当控制器中发生异常时,Spring Boot会在@ControllerAdvice或@RestControllerAdvice注解的类中查找与异常匹配的@ExceptionHandler注解标记的方法,并执行该方法来处理异常。

@ExceptionHandler注解可以定义一个或多个异常类型,并将它们映射到对应的异常处理方法。当控制器中发生指定类型的异常时,SpringBoot会自动调用对应的异常处理方法,并将异常对象传递给该方法作为参数。

一般的全局异常处理类

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final String DEFAULT_ERROR_MESSAGE = "系统繁忙,请稍后再试";
    @ExceptionHandler(value = {SQLIntegrityConstraintViolationException.class})
    public DataVO handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        log.error(e.getMessage(), e);
        return new DataVO(SysConstant.CODE_ERROR, "当前主键存在于其他表的外键约束,请先处理父表中的该外键");
    }
    @ExceptionHandler(value = {FileNotFoundException.class})
    public DataVO handleFileNotFoundException(FileNotFoundException e) {
        log.error(e.getMessage(), e);
        return new DataVO(SysConstant.CODE_ERROR, "路径不存在");
    }
    @ExceptionHandler(value = {Exception.class})
    public DataVO handleException(Exception e) {
        log.error(e.getMessage(), e);
        return new DataVO(SysConstant.CODE_ERROR, DEFAULT_ERROR_MESSAGE);
    }
}

如例所示,使用@RestControllerAdvice注解声明了GlobalExceptionHandler作为全局异常处理类,在这个类中的方法使用@ExceptionHandler注解指定某个方法处理对应的错误类型。

在这个一般的全局处理类中,@ExceptionHandler注解会根据异常类型选择最精确的处理方法进行处理,如果没有找到对应的处理方法,则会选择更加通用的处理方法。这样的处理方式会导致代码重复,每个处理方法都需要写一遍通用的操作,例如日志记录。

改进全局异常处理类

为了解决这个问题,我们可以将通用的异常处理逻辑抽象到一个方法中,并在处理方法中调用该方法

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final String DEFAULT_ERROR_MESSAGE = "系统繁忙,请稍后再试";
    @ExceptionHandler(value = {SQLIntegrityConstraintViolationException.class})
    public DataVO handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        log.error(e.getMessage(), e);
        return handleException(DEFAULT_ERROR_MESSAGE, e);
    }
    @ExceptionHandler(value = {FileNotFoundException.class})
    public DataVO handleFileNotFoundException(FileNotFoundException e) {
        log.error(e.getMessage(), e);
        return handleException(DEFAULT_ERROR_MESSAGE, e);
    }
    @ExceptionHandler(value = {Exception.class})
    public DataVO handleException(Exception e) {
        log.error(e.getMessage(), e);
        return handleException(DEFAULT_ERROR_MESSAGE, e);
    }
    private DataVO handleException(String defaultMessage, Throwable e) {
        if (e instanceof BusinessException) {
            return new DataVO(SysConstant.CODE_ERROR, e.getMessage());
        } else if (e instanceof MethodArgumentNotValidException) {
            return new DataVO(SysConstant.CODE_ERROR, ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage());
        } else {
            return new DataVO(SysConstant.CODE_ERROR, defaultMessage);
        }
    }
}

在这个示例中,定义了一个私有方法handleException,该方法接受一个默认错误消息和一个Throwable对象作为参数,并返回一个DataVO对象。

在处理方法中,调用handleException方法来处理异常,并将默认错误消息作为参数传递给handleException方法。

这样,我们就可以将通用的异常处理逻辑抽象到一个方法中,避免了重复代码。同时,我们也可以在handleException方法中添加自己的逻辑,例如记录日志等。

优雅的全局异常处理类

handleException方法中的if-else语句太过臃肿,可以使用Map来优化这个方法

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final String errorMsg = SysConstant.DEFAULT_ERROR;
    private static final Map<Class<? extends Throwable>, Function<Throwable, DataVO<Null>>> EXCEPTION_HANDLER_MAP = new HashMap<>();
    static {
        EXCEPTION_HANDLER_MAP.put(RuntimeException.class, e -> new DataVO<>(SysConstant.CODE_ERROR, e.getMessage()));
        EXCEPTION_HANDLER_MAP.put(DataIntegrityViolationException.class, e -> new DataVO<>(SysConstant.CODE_ERROR, "当前主键存在于其他表的外键约束,请先处理父表中的该外键"));
        EXCEPTION_HANDLER_MAP.put(FileNotFoundException.class, e -> new DataVO<>(SysConstant.CODE_ERROR, "路径不存在"));
    }
    @ExceptionHandler(Exception.class)
    public DataVO<Null> handleException(Exception e) {
        log.error(e.toString());
        return EXCEPTION_HANDLER_MAP.getOrDefault(e.getClass(), t -> new DataVO<>(SysConstant.CODE_ERROR, errorMsg)).apply(e);
    }
}

在这个示例中,使用Map来存储异常处理函数,每个函数都接受一个Throwable对象作为参数,并返回一个DataVO对象。

在处理方法中,根据异常类型从Map中获取对应的处理函数,如果没有找到对应的处理函数,则使用默认的处理函数。

然后,调用获取到的处理函数来处理异常。这样,就可以将异常处理函数集中在一个Map中,避免了if-else语句的臃肿。同时,也可以在异常处理函数handleException中添加自己的逻辑,例如记录日志等。

我的通用值对象类DataVO(Data Value Object)

@Data
//@JsonPropertyOrder({"code","msg","count","data"})//指定返回给前端的字段顺序
public class DataVO<T> {
    private Integer code = 0;
    private String msg = "";
    private Long count;
    private List<T> data;
    public DataVO() {
    }
    public DataVO(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public DataVO(Integer code, String msg, Long count, List<T> data) {
        this.code = code;
        this.msg = msg;
        this.count = count;
        this.data = data;
    }
}

遇到问题

在测试的时候要处理外键约束报错问题,控制台发现报错原因是SQLIntegrityConstraintViolationException,于是我在静态异常Map中加入了SQLIntegrityConstraintViolationException处理,但是发现并没有正常处理,还是返回的默认异常信息。

于是在控制台输出了一下那个错误的类,e.getClass()发现实际上是DataIntegrityViolationException,SQLIntegrityConstraintViolationException只是cause,改完之后测试成功处理异常并返回特定异常信息给前端。

到此这篇关于SpringBoot中@RestControllerAdvice注解全局异常处理类的文章就介绍到这了,更多相关@RestControllerAdvice注解全局异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring-data-redis 2.0 的使用示例代码

    spring-data-redis 2.0 的使用示例代码

    这篇文章主要介绍了spring-data-redis 2.0 的使用示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • Java 实现文件批量重命名亲测可用(精简版)

    Java 实现文件批量重命名亲测可用(精简版)

    本文给大家分享一段自己写的java代码实现文件批量重命名,亲测试过没有任何问题,大家可以放心使用
    2016-11-11
  • Java 实用工具类Spring 的 StopWatch常用方法

    Java 实用工具类Spring 的 StopWatch常用方法

    StopWatch 是 Spring 框架中极其实用的开发辅助工具,它为性能分析和调试提供了轻量但强大的能力,使用简单、无需复杂配置,本文给大家介绍Java 实用工具类Spring 的 StopWatch常用方法,感兴趣的朋友一起看看吧
    2025-04-04
  • java 接口回调实例详解

    java 接口回调实例详解

    这篇文章主要介绍了java 接口回调实例详解的相关资料,所谓回调就是使用java中的多态,需要的朋友可以参考下
    2017-07-07
  • springboot基于Mybatis mysql实现读写分离

    springboot基于Mybatis mysql实现读写分离

    这篇文章主要介绍了springboot基于Mybatis mysql实现读写分离,需要的朋友可以参考下
    2019-06-06
  • Java异常报错:java.nio.file.FileSystemException的多种解决方案

    Java异常报错:java.nio.file.FileSystemException的多种解决方案

    在Java应用程序中处理文件和目录时,java.nio.file.FileSystemException是一个常见的异常,这个异常发生在尝试进行文件系统操作时,本文将详细探讨FileSystemException的成因,并提供多种解决方案,需要的朋友可以参考下
    2024-12-12
  • Spring如何实现输出带动态标签的日志

    Spring如何实现输出带动态标签的日志

    文章介绍了如何通过动态标签日志实现,解决了部分业务代码在多个模块中调用时日志无法直观看出来源的问题,主要通过ThreadLocal存储业务标签,并在日志输出时插入该标签,实现日志的动态标签功能,感兴趣的朋友一起看看吧
    2024-12-12
  • struts2静态资源映射代码示例

    struts2静态资源映射代码示例

    这篇文章主要介绍了struts2静态资源映射的相关内容,涉及了具体代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • 深度deepin安装以及jdk、tomcat、Nginx安装教程

    深度deepin安装以及jdk、tomcat、Nginx安装教程

    这篇文章主要给大家介绍了关于深度deepin安装以及jdk、tomcat、Nginx安装的相关资料,按照文中介绍的方法可以轻松的实现安装,对大家的工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-01-01
  • RocketMQ设计之主从复制和读写分离

    RocketMQ设计之主从复制和读写分离

    这篇文章主要介绍了RocketMQ设计之主从复制和读写分离,RocketMQ提高消费避免Broker发生单点故障引起Broker上的消息无法及时消费,下文关于了RocketMQ的相关内容,需要的小伙伴可以参考一下
    2022-03-03

最新评论