为什么需要统一异常处理
没有统一异常处理时,Controller 里到处是 try-catch,前端拿到的错误格式五花八门:有的返回字符串,有的返回 500 裸异常,有的把堆栈直接抛给用户。Spring Boot 用 @RestControllerAdvice + @ExceptionHandler 可以把异常在一处收口,再配合统一返回体,前后端对接会清爽很多。
一、先定义统一返回结构
public class Result<T> {
private int code;
private String msg;
private T data;
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.code = 0; r.msg = "success"; r.data = data;
return r;
}
public static Result<?> fail(int code, String msg) {
Result<?> r = new Result<>();
r.code = code; r.msg = msg;
return r;
}
// getter/setter 省略
}
二、全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
// 业务异常
@ExceptionHandler(BusinessException.class)
public Result<?> handleBiz(BusinessException e) {
return Result.fail(e.getCode(), e.getMessage());
}
// 参数校验失败
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValid(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldError().getDefaultMessage();
return Result.fail(400, msg);
}
// 兜底:未知异常
@ExceptionHandler(Exception.class)
public Result<?> handleOther(Exception e) {
// 生产环境不要暴露堆栈
return Result.fail(500, "系统异常,请稍后重试");
}
}
三、业务异常与校验
自定义异常携带业务码:
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code, String msg) {
super(msg); this.code = code;
}
// getter
}
Controller 里直接抛,不用自己包:
@PostMapping("/order")
public Result<Order> create(@Valid @RequestBody CreateOrderReq req) {
if (req.getCount() <= 0)
throw new BusinessException(1001, "购买数量必须大于0");
return Result.ok(orderService.create(req));
}
@Valid 触发 Bean Validation,校验失败自动进 MethodArgumentNotValidException,返回字段级的友好提示。
四、几个注意点
- 生产环境别把异常堆栈返回前端,统一返回”系统异常”即可,细节记日志。
- 全局处理器建议放在独立的 package,确保被 Spring 扫描到。
- 如果用了
Filter/Interceptor里抛的异常,@RestControllerAdvice拦不到,需要在 Filter 层单独处理。
小结
统一异常处理的核心就一句话:异常在一处收口,返回格式只有一种。配合 Result<T> 和分层异常,前端再也不用写一堆 if 判断错误结构,联调效率直接翻倍。




