Spring 统一异常处理
非统一异常处理
在传统的非统一异常处理方式中,每个代码块都需要单独编写 try...catch...
语句来捕获异常,例如:
A代码: try...catch...
B代码: try...catch...
C代码: try...catch...
D代码: try...catch...
这种方式会导致代码冗余,并且难以维护。
统一异常处理
在 Spring 中,我们可以使用 AOP(面向切面编程)的思想来实现异常的统一处理。核心是利用 Spring 提供的两个注解 @RestControllerAdvice
和 @ExceptionHandler
。
任务步骤
整个统一异常处理的任务主要分为以下几个步骤:
- 自定义统一异常类
BizException
:规范异常类型,方便ExceptionHandler
进行处理。 - 前置异常捕获:对
controller
进行切面编程,实现对BindingResult
(即调用者请求参数)的统一异常/校验处理。 - 业务异常捕获:对
service
进行切面编程,捕获所有的service
异常,统一封装为BizException
,再返回给controller
,便于RestControllerAdvice
和ExceptionHandler
处理异常类型。 - 全局异常处理器:在全局异常处理器中,也就是
RestControllerAdvice
和ExceptionHandler
的处理方法中,封装响应结果并返回给调用者。
关键代码
依赖包
在 pom.xml
中添加以下依赖:
javax.validation
validation-api
2.0.1.Final
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
统一异常定义
自定义 BizException
类,继承自 RuntimeException
:
public class BizException extends RuntimeException {
private String code;
private String msg;
public BizException(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
前置异常捕获
创建 ControllerAspect
类,对 controller
进行切面编程:
/**
* 前置异常切面
*/
@Aspect
@Component
public class ControllerAspect {
private static final Logger log = LoggerFactory.getLogger(ControllerAspect.class);
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
"&&args(..,org.springframework.validation.BindingResult)")
public void controllerAspect() {
}
@Around("controllerAspect()")
public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof BindingResult) {
BindingResult result = (BindingResult) arg;
if (result.hasErrors()) {
log.error("请求参数校验错误");
FieldError fieldError = result.getFieldError();
if (fieldError != null) {
throw new BizException("111111", fieldError.getDefaultMessage());
}
throw new BizException("222222", "未知参数错误┭┮﹏┭┮");
}
}
}
return joinPoint.proceed();
}
}
业务异常捕获
创建 ServiceAspect
类,对 service
进行切面编程:
/**
* Service异常捕获,将Service中的所有异常,转换为统一的BizException,方便ExceptionHandler统一处理
*/
@Aspect
public class ServiceAspect {
@Pointcut("execution(public * com.example.exception.service.*.*(..))")
public void serviceAspect() {
}
@Around("serviceAspect()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Exception e) {
if (e instanceof BizException) {
throw e;
} else {
throw new BizException("999999", "系统错误^_^");
}
}
}
}
全局异常处理器
创建 GlobalExceptionHandler
类,处理全局异常:
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常处理
*/
@ExceptionHandler(BizException.class)
public ResponseEntity bizExceptionHandler(BizException e) {
return ResponseUtil.fail(e);
}
/**
* 通用异常处理
*/
@ExceptionHandler(Exception.class)
public ResponseEntity exceptionHandler(Exception e) {
log.error(e.getMessage(), e);
return ResponseUtil.fail("999999", "系统错误(#^.^#)");
}
}
通过以上步骤和代码,我们可以实现 Spring 中的统一异常处理,提高代码的可维护性和可读性。