SpringBoot统一异常处理的实用方案
作者:希望永不加班
在日常开发中,你一定见过这样的代码:代码冗余、维护困难、前端对接痛苦、日志混乱,线上出问题还难定位,统一异常处理,就是用来解决这些问题的,今天这篇文章,带你从零到一实现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统一异常处理的资料请关注脚本之家其它相关文章!
