掌握Spring Boot全局异常处理需理清“谁抛、谁拦、怎么包、怎么用”四步闭环:@RestControllerAdvice声明作用域,@ExceptionHandler精准捕获指定异常,@ResponseBody确保JSON响应;须构建分层异常体系(业务/系统/参数校验),统一响应体含code、message、timestamp、traceId;每个处理器须记录日志、设正确HTTP状态码、避免二次封装。

掌握 Spring Boot 全局异常处理,关键不在背注解,而在理清“谁抛、谁拦、怎么包、怎么用”这四步闭环。下面直击核心,不绕弯。
搞懂三个核心注解的真实分工
@RestControllerAdvice 不是“万能开关”,它是作用域声明:告诉 Spring “这个类负责拦截所有 @Controller 和 @RestController 中抛出的异常”。
@ExceptionHandler 是精准捕获器:必须指定异常类型(如 BusinessException.class),一个方法只管一类异常,不能写成 @ExceptionHandler(Exception.class) 包打天下(否则会吞掉本该 400 的参数校验异常)。
@ResponseBody(或直接用 @RestControllerAdvice)决定响应体格式——前后端分离项目必须返回 JSON,所以优先选 @RestControllerAdvice,省去每个方法加 @ResponseBody。
必须自定义分层异常体系
别再 throw new RuntimeException("用户不存在")。真实项目里要分清楚:
- 业务异常(4xx 类):如 UserNotFoundException、InsufficientBalanceException,继承自 BaseBusinessException,带明确 code(如 "USER_404")和用户可读 msg;
- 系统异常(5xx 类):如 DatabaseConnectionException、RedisTimeoutException,继承自 BaseSystemException,code 固定为 "SYS_ERROR",msg 统一为“系统繁忙,请稍后再试”;
- 参数校验异常(单独处理):@Valid 失败抛出 MethodArgumentNotValidException,需独立 @ExceptionHandler 捕获,提取 BindingResult 中的错误字段和提示。
统一响应体要带业务语义,不是套壳
Result
- code:字符串类型,如 "AUTH_TOKEN_EXPIRED",便于前端 switch 分支处理;
- message:面向用户的友好文案,不暴露技术细节;
- timestamp:毫秒时间戳,配合 traceId 用于日志溯源;
- requestId 或 traceId:从 MDC 获取,串联全链路日志;
- (可选)details:仅限内部日志使用,不返回给前端,比如 SQL 错误片段、堆栈摘要。
异常处理器里必须做三件事
每个 @ExceptionHandler 方法不是只返回 JSON 就完事:
- 记录完整日志:用 log.error("业务异常 [traceId={}][code={}]: {}", MDC.get("traceId"), e.getCode(), e.getMessage(), e);
- 设置正确 HTTP 状态码:业务异常用 400,系统异常用 500,认证失败用 401,权限不足用 403;
- 避免二次封装错误:不要在 handler 里再 try-catch 或手动 new ResponseEntity,直接 return Result.fail(...) 或 ResponseEntity.ok().body(...) 即可。


















