Springboot核心:统一异常处理

概述

统一异常处理机制在 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

相关推荐
小蒜学长4 分钟前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
brzhang13 分钟前
为什么 OpenAI 不让 LLM 生成 UI?深度解析 OpenAI Apps SDK 背后的新一代交互范式
前端·后端·架构
EnCi Zheng31 分钟前
JPA 连接 PostgreSQL 数据库完全指南
java·数据库·spring boot·后端·postgresql
brzhang37 分钟前
OpenAI Apps SDK ,一个好的 App,不是让用户知道它该怎么用,而是让用户自然地知道自己在做什么。
前端·后端·架构
LucianaiB1 小时前
从玩具到工业:基于 CodeBuddy code CLI 构建电力变压器绕组短路智能诊断系统
后端
_extraordinary_2 小时前
Java SpringBoot(一)--- 下载Spring相关插件,创建一个Spring项目,创建项目出现的问题
java·spring boot·spring
ruleslol2 小时前
SpringBoot14-ThreadLocal讲解
spring boot
武子康2 小时前
大数据-118 - Flink 批处理 DataSet API 全面解析:应用场景、代码示例与优化机制
大数据·后端·flink
不要再敲了2 小时前
Spring Security 完整使用指南
java·后端·spring
IT_陈寒2 小时前
Redis 高性能缓存设计:7个核心优化策略让你的QPS提升300%
前端·人工智能·后端