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注解全局异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 面试中遇到的java逃逸分析问题

    面试中遇到的java逃逸分析问题

    这篇文章主要介绍了面试中遇到的java逃逸分析问题,逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。,需要的朋友可以参考下
    2019-06-06
  • 一文教你学会搭建SpringBoot分布式项目

    一文教你学会搭建SpringBoot分布式项目

    这篇文章主要为大家详细介绍了搭建SpringBoot分布式项目的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • SpringBoot使用 Sleuth 进行分布式跟踪的过程分析

    SpringBoot使用 Sleuth 进行分布式跟踪的过程分析

    Spring Boot Sleuth是一个分布式跟踪解决方案,它可以帮助您在分布式系统中跟踪请求并分析性能问题,Spring Boot Sleuth是Spring Cloud的一部分,它提供了分布式跟踪的功能,本文将介绍如何在Spring Boot应用程序中使用Sleuth进行分布式跟踪,感兴趣的朋友一起看看吧
    2023-10-10
  • java 中使用maven shade plugin 打可执行Jar包

    java 中使用maven shade plugin 打可执行Jar包

    这篇文章主要介绍了java 中使用maven shade plugin 打可执行Jar包的相关资料,需要的朋友可以参考下
    2017-05-05
  • Spring Data JPA 注解Entity关联关系使用详解

    Spring Data JPA 注解Entity关联关系使用详解

    这篇文章主要为大家介绍了Spring Data JPA 注解Entity关联关系使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Springboot引入多个yml方法(多种方案)

    Springboot引入多个yml方法(多种方案)

    SpringBoot默认加载的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活该文件这篇文章主要介绍了Springboot引入多个yml方法,需要的朋友可以参考下
    2019-10-10
  • 详解JAVA Spring 中的事件机制

    详解JAVA Spring 中的事件机制

    这篇文章主要介绍了JAVA Spring 中的事件机制的相关资料,文中示例代码非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • spring/springboot整合dubbo详细教程

    spring/springboot整合dubbo详细教程

    今天教大家如何使用spring/springboot整合dubbo,文中有非常详细的图文介绍及代码示例,对正在学习java的小伙伴有很好地帮助,需要的朋友可以参考下
    2021-05-05
  • java累加和校验实现方式16进制(推荐)

    java累加和校验实现方式16进制(推荐)

    下面小编就为大家带来一篇java累加和校验实现方式16进制(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • SpringMVC实现获取请求参数方法详解

    SpringMVC实现获取请求参数方法详解

    Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet,Spring MVC 角色划分清晰,分工明细,这篇文章主要介绍了SpringMVC实现获取请求参数方法
    2022-09-09

最新评论