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

相关推荐
码兄科技5 小时前
实战:基于Spring Boot + UniApp的地理信息小程序开发
spring boot·后端·uni-app
腾讯云云开发7 小时前
腾讯云 CloudBase 登上 WAIC:我们为 Agent 重新设计了云的生产线
后端
星栈7 小时前
从装一堆工具到看懂 Node 工程化思维:我的项目复盘记录
后端·node.js
65岁退休Coder7 小时前
LangChain v1.3.4 笔记 - 05 Agent 上下文记忆
后端
颜酱7 小时前
04 | 召回前置准备:搭好召回所需的四个数据库
前端·人工智能·后端
JaneConan8 小时前
鸿蒙 韶非 UI 系列:能力调用 startAbilityForResult,跳能力拿回参,鸿蒙能力路由入门
后端·harmonyos
晴空了无痕8 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes
用户8356290780518 小时前
如何使用 Python 在 Excel 中添加、编辑和删除超链接
后端·python
花椒技术8 小时前
原本要 2 天的服务端冒烟前置审查,为什么 3 分半就能出报告?|QA 质量交付实践(三)
后端·ai编程·测试
达达尼昂8 小时前
AI 编程的工程化实践:Flutter AI Harness 的设计与落地
人工智能·后端·全栈