@ControllerAdvice+@ExceptionHandler可统一处理控制器层异常并标准化响应格式,但不覆盖过滤器、异步任务、Security异常及启动错误;需配合配置启用404/405拦截,并注意日志记录与敏感信息脱敏。

在 Spring Boot 项目中,用 @ControllerAdvice + @ExceptionHandler 可以统一拦截控制器层抛出的异常,实现全局错误响应格式标准化。它不处理过滤器、拦截器、异步线程或底层容器(如 Tomcat)抛出的错误,但覆盖了绝大多数业务异常场景。
定义统一响应结构
先设计一个通用的返回体,包含状态码、消息、时间戳和可选数据:
public class Result<T> {
private int code;
private String message;
private long timestamp;
private T data;
public static <T> Result<T> success(T data) {
Result<T> r = new Result<>();
r.code = 200;
r.message = "OK";
r.timestamp = System.currentTimeMillis();
r.data = data;
return r;
}
public static <T> Result<T> fail(int code, String message) {
Result<T> r = new Result<T>();
r.code = code;
r.message = message;
r.timestamp = System.currentTimeMillis();
return r;
}
// getter/setter 省略
}
编写全局异常处理器
创建一个被 @ControllerAdvice 注解的类,集中处理常见异常类型:
@ControllerAdvice
public class GlobalExceptionHandler {
// 捕获业务异常(比如自定义的 BusinessException)
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result<Void> handleBusinessException(BusinessException e) {
return Result.fail(e.getCode(), e.getMessage());
}
// 捕获参数校验失败(@Valid)
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Result<Void> handleValidationException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult()
.getFieldErrors()
.stream()
.map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
.collect(Collectors.joining("; "));
return Result.fail(400, "参数校验失败: " + msg);
}
// 捕获空指针等运行时异常(兜底,生产环境建议谨慎暴露细节)
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Result<Void> handleRuntimeException(RuntimeException e) {
// 日志记录很重要
log.error("未预期的运行时异常", e);
return Result.fail(500, "系统繁忙,请稍后再试");
}
// 捕获所有其他异常(最外层兜底)
@ExceptionHandler(Exception.class)
@ResponseBody
public Result<Void> handleException(Exception e) {
log.error("未捕获的异常", e);
return Result.fail(500, "服务内部错误");
}
}
- 每个
@ExceptionHandler方法按异常类型精准匹配,优先级由具体到宽泛 - 方法必须加
@ResponseBody(或类上加@RestControllerAdvice)才能返回 JSON - 务必记录日志,尤其是
RuntimeException和Exception,便于排查 - 不要在响应中直接返回敏感信息(如堆栈、数据库字段名),生产环境需脱敏
补充:处理 404、405 等 HTTP 状态码异常
@ControllerAdvice 默认不拦截 Spring MVC 的 404(NoHandlerFoundException)或 405(HttpRequestMethodNotSupportedException)。需要额外配置:
立即学习“Java免费学习笔记(深入)”;
- 在
application.yml中开启路径匹配:
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
- 然后在全局处理器中添加对应方法:
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseBody
public Result<Void> handle404(NoHandlerFoundException e) {
return Result.fail(404, "请求地址不存在");
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public Result<Void> handle405(HttpRequestMethodNotSupportedException e) {
return Result.fail(405, "请求方法不支持");
}
注意边界:它不处理什么?
这个方案覆盖的是 Spring MVC 控制器执行链路中的异常。以下情况需要其他方式处理:
- 过滤器(Filter)中抛出的异常 → 需在 Filter 内 try-catch 或配置 Servlet 容器错误页
- 全局异步任务(
@Async)→ 异常不会进入 ControllerAdvice,需用AsyncUncaughtExceptionHandler - Spring Security 认证/授权异常 → 应通过
AuthenticationEntryPoint和AccessDeniedHandler处理 - 启动阶段异常(如 Bean 创建失败)→ 属于 Spring 上下文初始化问题,无法被该处理器捕获


















