概述
统一异常处理机制在 Spring Boot 应用中是非常重要的核心点,因为它带来了多个方面的优势,能够显著提升应用的质量和开发效率。
为什么要优雅的处理异常
在后端发生异常或者是请求出错时,前端通常直接显示异常信息,而且对于敏感信息没有处理的情况会直接暴露出来,对于用户来说非常不友好。在以往的传统项目中,处理异常的方式是进行异常捕获处理,对于业务量比较多的项目就需要大量的try catch,工作量增多,代码看上去也比较冗余,统一异常处理就显得尤为重要。
统一异常处理的优势
提高代码的可维护性和整洁度 :通过将异常集中处理,可以避免重复的异常处理代码,使代码更加简洁、易于理解和维护。
统一响应格式 :统一的响应格式可以确保所有返回信息都以统一形式返回客户端,便于前端解析和用户体验的一致性。
增强应用程序的安全性和稳定性:统一只向外部展示错误提示,可以过滤掉可能暴露的内部结构和敏感信息。
统一异常处理使用
- 使用@ControllerAdvice 和 @ExceptionHandler:最常见且推荐的方式,适用于全局异常处理。通过创建一个带有 @ControllerAdvice 注解的类,并在其中定义多个使用 @ExceptionHandler 注解的方法来处理不同类型的异常。
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public Object handleResourceNotFoundException(ResourceNotFoundException ex) {
// 处理逻辑...
}
@ExceptionHandler(Exception.class)
public Object handleGenericException(Exception ex) {
// 处理逻辑...
}
}
- 使用@RestControllerAdvice注解和@ExceptionHandler:专门用于 RESTFUL API 的异常处理,可以确保返回JSON格式的错误信息。
java
@RestControllerAdvice
public class RestGlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public Object handleResourceNotFoundException(ResourceNotFoundException ex) {
// 处理逻辑...
}
}
□ 示例
- 定义统一响应类:
java
/**
* 响应结果类
*
* @param <T> 任意类型
*/
@Data
public class ResponseResult<T> {
/**
* 响应状态码,200是正常,非200表示异常
*/
private int status;
/**
* 异常编号
*/
private String errorCode;
/**
* 异常信息
*/
private String message;
/**
* 响应数据
*/
private T data;
public static <T> ResponseResult<T> success() {
return success(HttpServletResponse.SC_OK, null, null);
}
public static <T> ResponseResult<T> success(T data) {
return success(HttpServletResponse.SC_OK, null, data);
}
public static <T> ResponseResult<T> fail(String message) {
return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, message, null);
}
public static <T> ResponseResult<T> fail(String errorCode, String message) {
return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorCode, message, null);
}
public static <T> ResponseResult<T> success(int status, String message, T data) {
ResponseResult<T> r = new ResponseResult<>();
r.setStatus(status);
r.setMessage(message);
r.setData(data);
return r;
}
public static <T> ResponseResult<T> fail(int status, String errorCode, String message) {
return fail(status, errorCode, message, null);
}
public static <T> ResponseResult<T> fail(int status, String errorCode, String message, T data) {
ResponseResult<T> r = new ResponseResult<>();
r.setStatus(status);
r.setErrorCode(errorCode);
r.setMessage(message);
r.setData(data);
return r;
}
}
- 定义异常基类:
java
/**
* 异常基类,支持参数化的异常信息
*/
@Getter
@Setter
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 所属模块
*/
private String module;
/**
* 错误码
*/
private String code;
/**
* 错误码对应的参数
*/
private Object[] args;
/**
* 错误消息
*/
private String defaultMessage;
public BaseException(String module, String code, Object[] args, String defaultMessage) {
this.module = module;
this.code = code;
this.args = args;
this.defaultMessage = defaultMessage;
}
public BaseException(String module, String code, Object[] args) {
this(module, code, args, null);
}
public BaseException(String module, String defaultMessage) {
this(module, null, null, defaultMessage);
}
public BaseException(String code, Object[] args) {
this(null, code, args, null);
}
public BaseException(String defaultMessage) {
this(null, null, null, defaultMessage);
}
@Override
public String getMessage() {
String message = null;
if (!StringUtils.isEmpty(code)) {
message = MessageUtils.message(code, args);
}
if (message == null) {
message = defaultMessage;
}
return message;
}
}
- 定义业务异常:
java
/**
* 业务异常
*/
@Getter
@Setter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
public BusinessException(String message) {
this.message = message;
}
public BusinessException(String message, Integer code) {
this.message = message;
this.code = code;
}
public BusinessException(String message, Throwable e) {
super(message, e);
this.message = message;
}
}
- 定义异常枚举:
java
/**
* 异常枚举类
*/
public enum ExceptionEnum {
// 400
BAD_REQUEST("400", "请求数据格式不正确!"),
UNAUTHORIZED("401", "登录凭证过期!"),
FORBIDDEN("403", "没有访问权限!"),
NOT_FOUND("404", "请求的资源找不到!"),
// 500
INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
SERVICE_UNAVAILABLE("503", "服务器正忙,请稍后再试!"),
// 未知异常
UNKNOWN("10000", "未知异常!"),
// 自定义
IS_NOT_NULL("10001","%s不能为空");
/**
* 错误码
*/
private String code;
/**
* 错误描述
*/
private String msg;
ExceptionEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
- 异常处理:
java
import com.example.demo.model.ResponseResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler{
/**
* 基础异常
*/
@ExceptionHandler(BaseException.class)
public Object handler(Exception e) {
return ResponseResult.fail(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(BusinessException.class)
public Object handler(NullPointerException e) {
return ResponseResult.fail("发⽣NullPointerException:"+e.getMessage());
}
}
☆ 参考 :文章 FC464782123