Spring全家桶之全局异常处理
全局异常处理
- 全局异常捕获链路图
- 完美的异常处理方案
全局异常捕获链路图
┌───────────────────────────────┐
│ 客户端请求 │
└───────────────────────────────┘│▼┌──────────────────────┐│ Spring Cloud Gateway │ ← 全局异常处理: ErrorWebExceptionHandler│ (限流/鉴权/路由) │└──────────────────────┘│▼┌───────────────────────────────┐│ 微服务 A(Spring Boot) │└───────────────────────────────┘│┌─────────────────────────┐│ DispatcherServlet ││ (Spring MVC 内核) │└─────────────────────────┘│▼┌─────────────────────────┐│ Filter / Interceptor │ ← 全局异常处理: 自定义 Filter 兜底└─────────────────────────┘│▼┌─────────────────────────┐│ Controller 层 ││ 调用 Service / DAO │└─────────────────────────┘│▼┌─────────────────────────┐│ Service 层 ││ 抛 BizException 等异常 │└─────────────────────────┘│▼┌───────────────────────────────┐│ @RestControllerAdvice │ ← 主战场: 捕获业务异常/参数异常│ + @ExceptionHandler │└───────────────────────────────┘│▼┌───────────────────────────────┐│ HandlerExceptionResolver │ ← Spring 兜底解析器└───────────────────────────────┘│▼┌───────────────────────────────┐│ /error (ErrorController) │ ← 最终兜底: 保证返回统一 JSON└───────────────────────────────┘│▼
┌─────────────────────────────────────────┐
│ 返回统一 JSON 响应给客户端 │
└─────────────────────────────────────────┘┌──────────────────────────────────────────┐│ 横向扩展 ││ Feign 调用异常 → Feign ErrorDecoder ││ Hystrix/Sentinel 异常 → 特殊 Handler │└──────────────────────────────────────────┘
完美的异常处理方案
- Spring(最底层机制)
1️⃣ 业务层异常(Service 层)
不要在 Service 层吞异常,抛出自定义业务异常:
public class BizException extends RuntimeException {private final int code;public BizException(int code, String message) {super(message);this.code = code;}public int getCode() { return code; }
}
2️⃣ Controller 层异常
用 @RestControllerAdvice + @ExceptionHandler 作为主战场:
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BizException.class)public ApiResponse<?> handleBizException(BizException e) {return ApiResponse.error(e.getCode(), e.getMessage());}@ExceptionHandler(MethodArgumentNotValidException.class)public ApiResponse<?> handleValidationException(MethodArgumentNotValidException e) {String message = e.getBindingResult().getFieldError().getDefaultMessage();return ApiResponse.error(400, message);}@ExceptionHandler(Exception.class)public ApiResponse<?> handleException(Exception e) {return ApiResponse.error(500, "服务器异常:" + e.getMessage());}
}
3️⃣ Filter / Interceptor 层异常
防止 SpringMVC 之外的异常漏掉:
@Component
public class GlobalExceptionFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;try {chain.doFilter(req, res);} catch (Exception e) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json;charset=UTF-8");response.getWriter().write(JSON.toJSONString(ApiResponse.error(500, "过滤器捕获异常:" + e.getMessage())));}}
}
4️⃣ 最终兜底:ErrorController
如果前面都没捕获,Spring Boot 会走 /error,我们自定义:
@RestController
public class CustomErrorController implements ErrorController {@RequestMapping("/error")public ApiResponse<?> handleError(HttpServletRequest request) {return ApiResponse.error(500, "系统未知错误");}
}
5️⃣ 统一响应体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {private int code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(0, "ok", data);}public static <T> ApiResponse<T> error(int code, String message) {return new ApiResponse<>(code, message, null);}
}
- Spring Boot(单体应用主战场)
- 自定义业务异常
public class BizException extends RuntimeException {private final int code;public BizException(int code, String message) {super(message);this.code = code;}public int getCode() { return code; }
}
- 统一响应体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {private int code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(0, "ok", data);}public static <T> ApiResponse<T> error(int code, String message) {return new ApiResponse<>(code, message, null);}
}
- 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BizException.class)public ApiResponse<?> handleBizException(BizException e) {return ApiResponse.error(e.getCode(), e.getMessage());}@ExceptionHandler(MethodArgumentNotValidException.class)public ApiResponse<?> handleValidationException(MethodArgumentNotValidException e) {String message = e.getBindingResult().getFieldError().getDefaultMessage();return ApiResponse.error(400, message);}@ExceptionHandler(Exception.class)public ApiResponse<?> handleException(Exception e) {return ApiResponse.error(500, "服务器异常:" + e.getMessage());}
}
- Filter 层兜底
@Component
public class GlobalExceptionFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;try {chain.doFilter(req, res);} catch (Exception e) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json;charset=UTF-8");response.getWriter().write(JSON.toJSONString(ApiResponse.error(500, "过滤器捕获异常:" + e.getMessage())));}}
}
- 最终兜底 ErrorController
@Component
public class FeignErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String methodKey, Response response) {try {String body = IOUtils.toString(response.body().asInputStream(), StandardCharsets.UTF_8);ApiResponse<?> error = JSON.parseObject(body, ApiResponse.class);return new BizException(error.getCode(), error.getMessage());} catch (Exception e) {return new BizException(500, "远程服务调用失败");}}
}
- Spring Cloud(微服务扩展)
- Feign 调用异常处理
@Component
public class FeignErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String methodKey, Response response) {try {String body = IOUtils.toString(response.body().asInputStream(), StandardCharsets.UTF_8);ApiResponse<?> error = JSON.parseObject(body, ApiResponse.class);return new BizException(error.getCode(), error.getMessage());} catch (Exception e) {return new BizException(500, "远程服务调用失败");}}
}
- Gateway 全局异常处理(WebFlux)
@Component
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {@Overridepublic Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);response.getHeaders().setContentType(MediaType.APPLICATION_JSON);ApiResponse<?> error = ApiResponse.error(500, ex.getMessage());DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(error).getBytes(StandardCharsets.UTF_8));return response.writeWith(Mono.just(buffer));}
}
- 熔断 / 限流(Sentinel/Hystrix)异常处理
@ExceptionHandler(BlockException.class)
public ApiResponse<?> handleSentinelException(BlockException e) {return ApiResponse.error(429, "请求过多,请稍后再试");
}