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

相关文章

  • request如何获取完整url(包括域名、端口、参数)

    request如何获取完整url(包括域名、端口、参数)

    这篇文章主要介绍了request如何获取完整url(包括域名、端口、参数)问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • JVM中的flag设置详解

    JVM中的flag设置详解

    这篇文章主要介绍了JVM中的flag设置详解,涉及堆大小设置,收集器设置等香公馆内容,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Spring Security认证器实现过程详解

    Spring Security认证器实现过程详解

    一些权限框架一般都包含认证器和决策器,前者处理登陆验证,后者处理访问资源的控制,这篇文章主要介绍了Spring Security认证器实现过程,需要的朋友可以参考下
    2022-06-06
  • 详解java封装继承多态

    详解java封装继承多态

    这篇文章主要介绍了java封装继承多态,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Jsoup获取全国地区数据属性值(省市县镇村)

    Jsoup获取全国地区数据属性值(省市县镇村)

    这篇文章主要介绍了Jsoup获取全国地区数据属性值(省市县镇村)的相关资料,需要的朋友可以参考下
    2015-10-10
  • Spring Boot 在启动时进行配置文件加解密的方法详解

    Spring Boot 在启动时进行配置文件加解密的方法详解

    这篇文章主要介绍了Spring Boot 在启动时进行配置文件加解密的方法,本文通过实例给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • Java中的CyclicBarrier同步屏障详解

    Java中的CyclicBarrier同步屏障详解

    这篇文章主要介绍了Java中的CyclicBarrier同步屏障详解,CyclicBarrier也叫同步屏障,在JDK1.5被引入,可以让一组线程达到一个屏障时被阻塞,直到最后一个线程达到屏障时,屏障才会开门,所有被阻塞的线程才会继续执行,需要的朋友可以参考下
    2023-09-09
  • Java之多个线程顺序循环执行的几种实现

    Java之多个线程顺序循环执行的几种实现

    这篇文章主要介绍了Java之多个线程顺序循环执行的几种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 查看Spring容器中bean的五种方法小结

    查看Spring容器中bean的五种方法小结

    近期在写Spring项目的时候,需要通过注解的形式去替代之前直接将Bean存放在Spring容器这种方式,以此来简化对于Bean对象的操作,这篇文章主要给大家介绍了关于如何查看Spring容器中bean的五种方法,需要的朋友可以参考下
    2024-05-05
  • Java JTable 实现日历的示例

    Java JTable 实现日历的示例

    这篇文章主要介绍了Java JTable 实现日历的示例,帮助大家更好的理解和学习Java jtable的使用方法,感兴趣的朋友可以了解下
    2020-10-10

最新评论