SpringBoot统一异常处理的实用方案

 更新时间:2026年03月25日 09:17:30   作者:希望永不加班  
在日常开发中,你一定见过这样的代码:代码冗余、维护困难、前端对接痛苦、日志混乱,线上出问题还难定位,统一异常处理,就是用来解决这些问题的,今天这篇文章,带你从零到一实现SpringBoot全局统一异常处理,需要的朋友可以参考下

前言

在日常开发中,你一定见过这样的代码:

try {
    // 业务逻辑
} catch (Exception e) {
    e.printStackTrace();
}

或者接口返回五花八门:

  • 有的返回 500 错误
  • 有的返回一段文字
  • 有的直接抛异常前端看不懂
  • 每个接口都写一遍 try-catch

代码冗余、维护困难、前端对接痛苦、日志混乱,线上出问题还难定位。

统一异常处理,就是用来解决这些问题的。

今天这篇文章,带你从零到一实现 Spring Boot 全局统一异常处理

包含:自定义异常、统一返回、全局捕获、异常分类、日志规范、实战案例。

看完直接落地到你的项目里。

一、为什么要做统一异常处理?

1. 现状痛点

  • 每个接口都写 try-catch,代码重复
  • 异常信息不规范,前端无法统一解析
  • 错误信息暴露给用户,不安全
  • 日志混乱,难以排查问题
  • 系统稳定性差,容易直接崩接口

2. 统一异常处理的好处

  • 代码更优雅,业务层不用处理异常
  • 所有异常统一格式返回
  • 异常可监控、可统计、可告警
  • 安全:不把系统异常暴露给前端
  • 提升开发效率与维护性

二、实现统一异常处理的核心组件

Spring Boot 提供两个最核心注解:

  • @RestControllerAdvice:全局捕获控制器异常
  • @ExceptionHandler:捕获指定类型异常

配合:

  • 统一返回结果类
  • 自定义业务异常
  • 异常分类
  • 日志规范

就能完成一套企业级异常体系。

三、第一步:定义统一返回格式

前端最需要的是固定结构

创建 Result.java

import lombok.Data;
@Data
public class Result<T> {
    private int code;
    private String msg;
    private T data;
    // 成功
    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.setCode(200);
        r.setMsg("success");
        r.setData(data);
        return r;
    }
    // 失败
    public static <T> Result<T> fail(int code, String msg) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(null);
        return r;
    }
}

以后所有接口统一返回 Result

四、第二步:自定义业务异常

系统异常和业务异常要分开。

创建 BusinessException.java

public class BusinessException extends RuntimeException {
    private int code;
    private String msg;
    public BusinessException(String msg) {
        super(msg);
        this.code = 500;
        this.msg = msg;
    }
    public BusinessException(int code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
    // getter
}

使用示例:

if (user == null) {
    throw new BusinessException(400, "用户不存在");
}

五、第三步:定义全局异常捕获类(核心)

创建 GlobalExceptionHandler.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 捕获业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        log.warn("业务异常:{}", e.getMsg());
        return Result.fail(e.getCode(), e.getMsg());
    }
    /**
     * 捕获参数校验异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleIllegalArgument(IllegalArgumentException e) {
        log.warn("参数异常:{}", e.getMessage());
        return Result.fail(400, e.getMessage());
    }
    /**
     * 捕获空指针
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<?> handleNullPointer(NullPointerException e) {
        log.error("空指针异常", e);
        return Result.fail(500, "服务器内部错误");
    }
    /**
     * 兜底:所有其他异常
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        log.error("系统异常", e);
        return Result.fail(500, "服务器繁忙,请稍后再试");
    }
}

作用:

所有 Controller 抛出的异常,都会进入这里统一处理。

六、第四步:整合 Validation 参数校验(非常实用)

前端传参错误,统一返回。

1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. DTO 加校验

import jakarta.validation.constraints.NotBlank;
public class UserLoginDTO {
    @NotBlank(message = "用户名不能为空")
    private String username;
    @NotBlank(message = "密码不能为空")
    private String password;
}

3. 接口加@Valid

@PostMapping("/login")
public Result<?> login(@Valid @RequestBody UserLoginDTO dto) {
    return Result.success("login success");
}

4. 捕获校验异常

import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
@ExceptionHandler(BindException.class)
public Result<?> handleBindException(BindException e) {
    FieldError fieldError = e.getFieldError();
    String msg = fieldError.getDefaultMessage();
    log.warn("参数校验失败:{}", msg);
    return Result.fail(400, msg);
}

效果:

前端收到干净的:

{
  "code": 400,
  "msg": "用户名不能为空",
  "data": null
}

七、第五步:规范日志级别(线上必备)

  • 业务异常:warn
  • 参数异常:warn
  • 系统异常:error

示例:

log.warn("业务异常:{}", e.getMsg());
log.error("空指针异常", e);

便于日志平台筛选、告警。

八、第六步:实际业务中怎么用?(最真实示例)

Service 层:

@Service
public class UserService {
    public User login(String username, String password) {
        User user = userMapper.selectByUsername(username);
        if (user == null) {
            throw new BusinessException(400, "用户名或密码错误");
        }
        if (!user.getPassword().equals(password)) {
            throw new BusinessException(400, "用户名或密码错误");
        }
        return user;
    }
}

Controller 层:

@PostMapping("/login")
public Result<User> login(@Valid @RequestBody UserLoginDTO dto) {
    User user = userService.login(dto.getUsername(), dto.getPassword());
    return Result.success(user);
}

没有 try-catch!

代码极度清爽。

九、企业级异常分类规范

你可以直接在项目里使用这套异常码:

  • 200 成功
  • 400 参数错误
  • 401 未登录
  • 403 无权限
  • 404 不存在
  • 500 系统错误
  • 1001~1999 业务自定义

示例:

throw new BusinessException(401, "请先登录");
throw new BusinessException(403, "无此权限");
throw new BusinessException(1001, "余额不足");

以上就是SpringBoot统一异常处理的实用方案的详细内容,更多关于SpringBoot统一异常处理的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Spring中的@Scope注解

    详解Spring中的@Scope注解

    这篇文章主要介绍了详解Spring中的@Scope注解,@Scope注解是Spring IOC容器中的一个作用域,在Spring IOC容器中,他用来配置Bean实例的作用域对象,需要的朋友可以参考下
    2023-07-07
  • Springboot如何添加server.servlet.context-path相关使用

    Springboot如何添加server.servlet.context-path相关使用

    这篇文章主要介绍了Springboot如何添加server.servlet.context-path相关使用问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 解决IDEA service层跳转实现类的快捷图标消失问题

    解决IDEA service层跳转实现类的快捷图标消失问题

    这篇文章主要介绍了解决IDEA service层跳转实现类的快捷图标消失问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • @Column映射不一致的解决

    @Column映射不一致的解决

    这篇文章主要介绍了@Column映射不一致的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • java如何从地址串中解析提取省市区(完美匹配中国所有地址)

    java如何从地址串中解析提取省市区(完美匹配中国所有地址)

    这篇文章主要给大家介绍了关于java如何从地址串中解析提取省市区的相关资料,通过这个方法可以完美匹配中国所有地址,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • 使用java对一副扑克牌建模

    使用java对一副扑克牌建模

    这篇文章主要为大家详细介绍了如何使用java对一副扑克牌建模,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 举例讲解Java的Jackson库中ObjectMapper类的使用

    举例讲解Java的Jackson库中ObjectMapper类的使用

    这篇文章主要介绍了举例讲解Java的Jackson库中ObjectMapper类的使用,Jackson库通常被用来实现Java的对象和JSON之间的转换功能,需要的朋友可以参考下
    2016-01-01
  • SpringBoot中配置文件pom.xml的使用详解

    SpringBoot中配置文件pom.xml的使用详解

    SpringBoot的pom.xml文件是Maven项目的核心配置文件,用于定义项目的依赖、插件、构建配置等信息,下面小编就来和大家详细介绍一下它的具体使用吧
    2025-03-03
  • 浅谈一下Java中的内存模型JMM

    浅谈一下Java中的内存模型JMM

    这篇文章主要介绍了浅谈一下Java中的内存模型JMM,JMM,全程是 Java Memory Model ,直译就是 Java 内存模型,根据这个名字,可以知道它是 Java 设计用来管理内存的一个模型,需要的朋友可以参考下
    2023-08-08
  • Maven仓库分类的优先级

    Maven仓库分类的优先级

    本文主要介绍了Maven仓库分类的优先级,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04

最新评论