Spring统一异常处理

艺帆风顺 发布于 2025-04-03 31 次阅读


Spring 统一异常处理

非统一异常处理

在传统的非统一异常处理方式中,每个代码块都需要单独编写 try...catch... 语句来捕获异常,例如:

  1. A代码: try...catch...
  2. B代码: try...catch...
  3. C代码: try...catch...
  4. D代码: try...catch...

这种方式会导致代码冗余,并且难以维护。

统一异常处理

在 Spring 中,我们可以使用 AOP(面向切面编程)的思想来实现异常的统一处理。核心是利用 Spring 提供的两个注解 @RestControllerAdvice 和 @ExceptionHandler

任务步骤

整个统一异常处理的任务主要分为以下几个步骤:

  1. 自定义统一异常类 BizException:规范异常类型,方便 ExceptionHandler 进行处理。
  2. 前置异常捕获:对 controller 进行切面编程,实现对 BindingResult(即调用者请求参数)的统一异常/校验处理。
  3. 业务异常捕获:对 service 进行切面编程,捕获所有的 service 异常,统一封装为 BizException,再返回给 controller,便于 RestControllerAdvice 和 ExceptionHandler 处理异常类型。
  4. 全局异常处理器:在全局异常处理器中,也就是 RestControllerAdvice 和 ExceptionHandler 的处理方法中,封装响应结果并返回给调用者。

关键代码

依赖包

在 pom.xml 中添加以下依赖:

  1.     javax.validation
  2.     validation-api
  3.     2.0.1.Final
  4.     org.springframework.boot
  5.     spring-boot-starter-validation
  6.     org.springframework.boot
  7.     spring-boot-starter-aop
  8.     org.springframework.boot
  9.     spring-boot-starter-web
  10.     org.projectlombok
  11.     lombok
  12.     true

统一异常定义

自定义 BizException 类,继承自 RuntimeException

  1. public class BizException extends RuntimeException {
  2.     private String code;
  3.     private String msg;
  4.     public BizException(String code, String msg) {
  5.         this.code = code;
  6.         this.msg = msg;
  7.     }
  8.     public String getCode() {
  9.         return code;
  10.     }
  11.     public String getMsg() {
  12.         return msg;
  13.     }
  14. }

前置异常捕获

创建 ControllerAspect 类,对 controller 进行切面编程:

  1. /**
  2.  * 前置异常切面
  3.  */
  4. @Aspect
  5. @Component
  6. public class ControllerAspect {
  7.     private static final Logger log = LoggerFactory.getLogger(ControllerAspect.class);
  8.     @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
  9.             "&&args(..,org.springframework.validation.BindingResult)")
  10.     public void controllerAspect() {
  11.     }
  12.     @Around("controllerAspect()")
  13.     public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
  14.         Object[] args = joinPoint.getArgs();
  15.         for (Object arg : args) {
  16.             if (arg instanceof BindingResult) {
  17.                 BindingResult result = (BindingResult) arg;
  18.                 if (result.hasErrors()) {
  19.                     log.error("请求参数校验错误");
  20.                     FieldError fieldError = result.getFieldError();
  21.                     if (fieldError != null) {
  22.                         throw new BizException("111111", fieldError.getDefaultMessage());
  23.                     }
  24.                     throw new BizException("222222", "未知参数错误┭┮﹏┭┮");
  25.                 }
  26.             }
  27.         }
  28.         return joinPoint.proceed();
  29.     }
  30. }

业务异常捕获

创建 ServiceAspect 类,对 service 进行切面编程:

  1. /**
  2.  * Service异常捕获,将Service中的所有异常,转换为统一的BizException,方便ExceptionHandler统一处理
  3.  */
  4. @Aspect
  5. public class ServiceAspect {
  6.     @Pointcut("execution(public * com.example.exception.service.*.*(..))")
  7.     public void serviceAspect() {
  8.     }
  9.     @Around("serviceAspect()")
  10.     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  11.         try {
  12.             return joinPoint.proceed();
  13.         } catch (Exception e) {
  14.             if (e instanceof BizException) {
  15.                 throw e;
  16.             } else {
  17.                 throw new BizException("999999", "系统错误^_^");
  18.             }
  19.         }
  20.     }
  21. }

全局异常处理器

创建 GlobalExceptionHandler 类,处理全局异常:

  1. @RestControllerAdvice
  2. public class GlobalExceptionHandler {
  3.     private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  4.     /**
  5.      * 业务异常处理
  6.      */
  7.     @ExceptionHandler(BizException.class)
  8.     public ResponseEntity bizExceptionHandler(BizException e) {
  9.         return ResponseUtil.fail(e);
  10.     }
  11.     /**
  12.      * 通用异常处理
  13.      */
  14.     @ExceptionHandler(Exception.class)
  15.     public ResponseEntity exceptionHandler(Exception e) {
  16.         log.error(e.getMessage(), e);
  17.         return ResponseUtil.fail("999999", "系统错误(#^.^#)");
  18.     }
  19. }

通过以上步骤和代码,我们可以实现 Spring 中的统一异常处理,提高代码的可维护性和可读性。