1359 字
约 4 分钟
0
Spring Boot 全局异常处理:统一管理你的 API 错误

Spring Boot 全局异常处理:统一管理你的 API 错误

在开发 Spring Boot 后端接口时,全局异常处理 是一个非常重要且实用的特性。它能帮助我们:

  • 统一返回统一的错误格式
  • 减少大量的重复 try-catch
  • 提升接口的安全性和健壮性
  • 标准化错误提示,提高用户体验

为什么要使用全局异常处理?

1. 统一异常格式

不同业务场景下,异常抛出类型各不相同:

  • 表单验证失败抛 MethodArgumentNotValidException
  • 空指针异常抛 NullPointerException
  • 自定义业务异常抛 CustomException

使用全局异常处理后,所有异常都会返回一致的 JSON 格式(如:{"code": 500, "message": "服务器内部错误"}),便于前端统一处理。

2. 减少重复代码

每次写接口都要手动处理异常,不仅麻烦,而且容易遗漏。全局异常处理可以集中管理所有异常,避免重复代码。

3. 统一错误提示

把错误码、错误信息、错误类型等统一封装,便于前端根据不同的 code 做不同的处理(如:跳转到 404 页面、显示友好的提示等)。

4. 提升接口安全性

全局异常处理可以捕获所有可能抛出的异常(如:空指针、SQL 异常、IO 异常等),避免敏感信息泄露到前端。

5. 标准化错误码管理

通过自定义错误枚举,可以对所有业务错误进行统一管理,便于查询和维护。

自定义异常类

1. 业务异常类(推荐使用)

public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private int code;           // 自定义错误码
    private String message;     // 异常信息
    private Object data;        // 额外数据

    // 构造方法重载
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    public BusinessException(int code, String message, Object data) {
        super(message);
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public BusinessException(String message) {
        super(message);
        this.code = 500;
        this.message = message;
    }

    // Getter + Setter
    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public Object getData() {
        return data;
    }
}

2. 自定义错误枚举(推荐)

public enum ErrorCode {

    // 通用错误
    SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败"),
    PARAM_ERROR(1001, "参数错误"),
    UNAUTHORIZED(1002, "未授权"),
    FORBIDDEN(1003, "禁止访问"),
    NOT_FOUND(1004, "资源不存在"),
    SYSTEM_ERROR(5000, "服务器内部错误"),
    
    // 业务场景错误
    USER_NOT_FOUND(2001, "用户不存在"),
    PASSWORD_ERROR(2002, "密码错误"),
    ACCOUNT_DISABLED(2003, "账号已被禁用"),
    EMAIL_EXISTS(2004, "邮箱已被注册"),
    PHONE_EXISTS(2005, "手机号已被注册"),
    INVALID_TOKEN(2006, "无效的 Token"),
    TOKEN_EXPIRED(2007, "Token 已过期"),
    
    // 文件系统错误
    FILE_NOT_FOUND(3001, "文件不存在");

    private int code;
    private String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public static ErrorCode fromCode(int code) {
        for (ErrorCode errorCode : ErrorCode.values()) {
            if (errorCode.code == code) {
                return errorCode;
            }
        }
        return FAILED;
    }
}

全局异常处理器

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 自定义业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<Object> handleBusinessException(BusinessException ex) {
        return Result.failure(ex.getCode(), ex.getMessage());
    }

    // 自定义错误枚举异常
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<Object> handleIllegalArgumentException(IllegalArgumentException ex) {
        ErrorCode errorCode = ErrorCode.fromCode(Integer.parseInt(ex.getMessage()));
        return Result.failure(errorCode.getCode(), errorCode.getMessage());
    }

    // 表单验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        String message = bindingResult.getFieldError().getDefaultMessage();
        return Result.failure(1001, message);
    }

    // 请求参数校验异常
    @ExceptionHandler(BindException.class)
    public Result<Object> handleBindException(BindException ex) {
        String message = ex.getBindingResult().getFieldError().getDefaultMessage();
        return Result.failure(1001, message);
    }

    // 空指针异常
    @ExceptionHandler(NullPointerException.class)
    public Result<Object> handleNullPointerException(NullPointerException ex) {
        return Result.failure(5001, "空指针异常:" + ex.getMessage());
    }

    // 其他未知异常
    @ExceptionHandler(Exception.class)
    public Result<Object> handleException(Exception ex) {
        return Result.failure(5000, "服务器内部错误:" + ex.getMessage());
    }

    // 自定义返回工具类
    private static class Result<T> {
        private int code;
        private String message;
        private T data;

        private Result(int code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }

        public static <T> Result<T> success() {
            return new Result<>(200, "操作成功", null);
        }

        public static <T> Result<T> success(T data) {
            return new Result<>(200, "操作成功", data);
        }

        public static <T> Result<T> failure(int code, String message) {
            return new Result<>(code, message, null);
        }
    }
}

最佳实践

  1. 使用自定义异常:抛出 BusinessExceptionIllegalArgumentException,便于统一处理。
  2. 统一错误码:通过 ErrorCode 枚举统一管理所有错误码,便于维护和查询。
  3. 优先抛出运行时异常:如 RuntimeExceptionBusinessException 等,避免强制捕获。
  4. 日志记录:在全局异常处理器中打印异常堆栈,便于排查问题。
  5. 扩展性强:支持多种异常类型,方便后续扩展。

总结

全局异常处理是 Spring Boot 开发中非常重要的一个特性。通过自定义异常类、自定义错误枚举、全局异常处理器,我们可以实现对所有 API 错误的统一管理和优雅处理。

掌握全局异常处理后,你的接口将更加健壮、安全,前端调用也更加方便。

相关内链推荐

SEO 优化建议

  • 标题包含核心关键词:“Spring Boot”、“全局异常处理”、“自定义异常”、“业务异常”。
  • 建议添加更多代码截图,提升用户动手能力。
Spring Boot 全局异常处理:统一管理你的 API 错误
http://www.clxhxhhr.top/posts/331/
作者
clxstart
发布于
2026-08-21
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。