微服务架构中,统一异常处理和标准化响应格式是API接口规范的基础。Spring Boot提供了@RestControllerAdvice全局异常拦截机制,配合自定义异常体系可实现错误的精细化分类和友好响应。后端开发中,散落的try-catch块导致异常处理逻辑不一致,全局拦截器能将异常处理收敛到统一入口。本文基于Spring Boot框架实践,从异常体系设计到响应规范落地,拆解微服务统一错误处理的完整方案。
Spring Boot全局异常处理器配置
@RestControllerAdvice是Spring Boot 3.x中处理REST API异常的核心注解,通过@ExceptionHandler方法拦截Controller抛出的异常。基本配置:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponse<Void>> handleBusiness(
BusinessException ex, HttpServletRequest request) {
log.warn("业务异常: code={}, message={}, path={}",
ex.getCode(), ex.getMessage(), request.getRequestURI());
return ResponseEntity
.status(ex.getHttpStatus())
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Void>> handleValidation(
MethodArgumentNotValidException ex) {
List<FieldError> errors = ex.getBindingResult()
.getFieldErrors().stream()
.map(e -> new FieldError(
e.getField(),
e.getDefaultMessage(),
e.getRejectedValue()))
.collect(Collectors.toList());
log.warn("参数校验失败: {}", errors);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, "参数校验失败", errors));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleUnknown(
Exception ex, HttpServletRequest request) {
log.error("未处理异常: path={}", request.getRequestURI(), ex);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error(500, "系统内部错误"));
}
}
异常处理器的匹配顺序:Spring按异常类型从具体到通用匹配。BusinessException比Exception更具体,会优先匹配。多个@ExceptionHandler覆盖同一异常层级时,最具体的处理器生效。
自定义业务异常体系设计
异常体系设计遵循单一继承根、按业务域分类的原则。基础异常类定义通用字段,子类按业务场景细化:
@Getter
public class BusinessException extends RuntimeException {
private final int code;
private final HttpStatus httpStatus;
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.httpStatus = HttpStatus.BAD_REQUEST;
}
public BusinessException(int code, String message, HttpStatus httpStatus) {
super(message);
this.code = code;
this.httpStatus = httpStatus;
}
}
// 认证异常
public class AuthenticationException extends BusinessException {
public AuthenticationException(String message) {
super(401, message, HttpStatus.UNAUTHORIZED);
}
}
// 权限异常
public class AuthorizationException extends BusinessException {
public AuthorizationException(String message) {
super(403, message, HttpStatus.FORBIDDEN);
}
}
// 资源不存在
public class NotFoundException extends BusinessException {
public NotFoundException(String resource, Object id) {
super(404, resource + "不存在: " + id, HttpStatus.NOT_FOUND);
}
}
// 冲突异常(重复创建)
public class ConflictException extends BusinessException {
public ConflictException(String message) {
super(409, message, HttpStatus.CONFLICT);
}
}
// 限流异常
public class RateLimitException extends BusinessException {
public RateLimitException(String message) {
super(429, message, HttpStatus.TOO_MANY_REQUESTS);
}
}
业务代码中直接抛出语义化异常,无需手动包装响应:
@Service
public class OrderService {
public Order createOrder(OrderRequest req) {
User user = userRepository.findById(req.getUserId())
.orElseThrow(() -> new NotFoundException("用户", req.getUserId()));
if (orderRepository.existsByOrderNo(req.getOrderNo())) {
throw new ConflictException("订单号已存在: " + req.getOrderNo());
}
if (user.getStatus() != UserStatus.ACTIVE) {
throw new BusinessException(40010, "用户已被冻结");
}
Order order = new Order();
order.setOrderNo(req.getOrderNo());
order.setUser(user);
return orderRepository.save(order);
}
}
统一响应结果封装规范
统一响应体使用泛型支持不同场景的数据返回:
@Data
@Schema(description = "统一响应")
public class ApiResponse<T> {
@Schema(description = "状态码,0表示成功")
private int code;
@Schema(description = "提示信息")
private String message;
@Schema(description = "响应数据")
private T data;
@Schema(description = "时间戳")
private long timestamp = System.currentTimeMillis();
@Schema(description = "请求追踪ID")
private String traceId;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> resp = new ApiResponse<>();
resp.code = 0;
resp.message = "success";
resp.data = data;
return resp;
}
public static <T> ApiResponse<T> success(T data, String message) {
ApiResponse<T> resp = success(data);
resp.message = message;
return resp;
}
public static <T> ApiResponse<T> error(int code, String message) {
ApiResponse<T> resp = new ApiResponse<>();
resp.code = code;
resp.message = message;
return resp;
}
public static <T> ApiResponse<T> error(int code, String message, T data) {
ApiResponse<T> resp = error(code, message);
resp.data = data;
return resp;
}
}
Controller层配合统一响应,通过ResponseBodyAdvice自动包装成功响应,避免每个方法手动包装:
@RestControllerAdvice
public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class converterType) {
// 排除已经包装过和错误响应
return !returnType.getParameterType().equals(ApiResponse.class)
&& !returnType.getParameterType().equals(ResponseEntity.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
if (body == null) {
return ApiResponse.success(null);
}
return ApiResponse.success(body);
}
}
Controller方法直接返回业务数据,自动包装为统一格式:
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping("/{id}")
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id); // 自动包装为ApiResponse.success(order)
}
@PostMapping
public Order createOrder(@Valid @RequestBody OrderRequest req) {
return orderService.createOrder(req);
}
}
异常处理链路与参数校验集成
Spring Validation校验失败产生MethodArgumentNotValidException(表单校验)和ConstraintViolationException(方法参数校验),统一拦截处理:
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ApiResponse<Void>> handleConstraint(
ConstraintViolationException ex) {
List<FieldError> errors = ex.getConstraintViolations().stream()
.map(v -> new FieldError(
v.getPropertyPath().toString(),
v.getMessage(),
v.getInvalidValue()))
.collect(Collectors.toList());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, "参数校验失败", errors));
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ApiResponse<Void>> handleJsonParse(
HttpMessageNotReadableException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, "请求体格式错误"));
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<ApiResponse<Void>> handleMissingParam(
MissingServletRequestParameterException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, "缺少必要参数: " + ex.getParameterName()));
}
自定义校验注解配合异常体系,在业务层面补充Bean Validation无法覆盖的跨字段校验:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = OrderValidator.class)
public @interface ValidOrder {
String message() default "订单参数不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class OrderValidator
implements ConstraintValidator<ValidOrder, OrderRequest> {
@Override
public boolean isValid(OrderRequest req, ConstraintValidatorContext ctx) {
if (req == null) return false;
// 跨字段校验逻辑
return req.getEndDate() == null
|| req.getStartDate() == null
|| !req.getEndDate().isBefore(req.getStartDate());
}
}
生产环境异常日志与监控方案
异常日志分级记录:业务异常记录WARN级别,包含错误码和用户友好信息;系统异常记录ERROR级别,包含完整堆栈。通过MDC注入traceId实现链路追踪:
@Component
public class TraceIdFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String traceId = request.getHeader("X-Trace-Id");
if (traceId == null) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId);
response.setHeader("X-Trace-Id", traceId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
logback配置中使用traceId,日志中自动注入请求追踪ID,便于在全链路日志中关联同一次请求的所有异常记录。
异常监控告警:对ERROR级别异常接入告警系统,设置异常频次阈值。同一业务异常码在短时间内大量出现时触发告警,提示下游服务故障或参数问题。业务异常(4xx)频次监控用于发现API使用模式异常,如大量404可能表示前端调用路径错误。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-tong-yi-yi-chang-chu-li-yu-quan-ju/