Spring Boot全局异常统一处理

背景

以往的 Java Web 开发中,异常处理通常是通过 try-catch 语句块来实现。这种方法在应用程序规模较小的情况下还可以,但是在大型应用中,可能存在大量的代码重复和不一致问题。此外,在抛出未处理的异常时,用户会看到系统生成的默认错误页面,用户体验差。


全局异常统一处理

优点

  • 有助于保持代码整洁和规模化

    如果没有全局异常处理,每个 Controller 的方法都需要实现自己的异常处理,当程序变得越来越复杂时,这种代码会导致代码冗余和混乱的异常处理逻辑。

  • 提升用户体验

    全局异常处理 允许应用程序捕获未处理的异常,并提供更友好的异常提示信息

  • 便于日志记录和监控

    全局异常处理可以帮助应用程序捕获和记录异常信息,在出现问题时快速定位。此外,还可以和监控系统继承,以实时跟踪应用程序中出现的异常情况。

  • 增强安全性

    全局异常处理可以防止应用程序出现潜在的安全漏洞,例如 SQL 注入和 XSS 攻击。


实操

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

自定义一个异常类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GlobalException extends RuntimeException {
    private RespBeanEnum respBeanEnum;

    public RespBeanEnum getRespBeanEnum() {
        return respBeanEnum;
    }

    public void setRespBeanEnum(RespBeanEnum respBeanEnum) {
        this.respBeanEnum = respBeanEnum;
    }
}

编写统一异常处理类,统一捕获处理返回

@RestControllerAdvice // 表明统一处理异常
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class) // 希望捕获的异常类
    public RespBean exceptionHandler(Exception e) {
        if (e instanceof GlobalException ex) {
            return RespBean.error(ex.getRespBeanEnum());
        } else if (e instanceof MethodArgumentNotValidException ex) {
            RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
            respBean.setMessage("参数校验异常:" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            return respBean;
        }
        return RespBean.error(RespBeanEnum.ERROR);
    }
}

源码分析 @ExceptionHandler 如何捕获异常

@ExceptionHandler是Spring框架提供的机制,用于集中处理Controller中的异常。当Controller的方法抛出一个未被捕获的异常时,Spring会寻找合适的处理器。

  1. 异常首先被DispatcherServlet捕获。

    image-20240704154047345
  2. HandlerExceptionResolver处理异常

    image-20240704154652761

    最后getMappedMethod()方法会找到可以匹配处理异常的所有方法信息,根据能处理的异常类进行从小到大的排序,取匹配度最高的处理异常方法