深入解析:Spring Boot全局异常处理:一网打尽Controller层异常,@RestControllerAdvice解析

在Spring Boot应用中,异常处理是构建健壮API的核心环节。

一、为什么需要全局异常处理?

在传统Controller开发中,我们经常面临这样的困境:

@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
try {
return userService.findById(id);
} catch (UserNotFoundException e) {
// 重复的异常处理代码
return new ErrorResponse("用户不存在");
} catch (DatabaseException e) {
// 每个Controller都要处理相同异常
return new ErrorResponse("数据库错误");
}
}
}

痛点分析

  • 重复代码遍布各个Controller

  • 异常处理与业务逻辑耦合

  • 响应格式不统一

  • 维护成本高

二、@RestControllerAdvice 

1. 注解关系

2. 核心组件协作

三、Spring Boot 3 全局异常处理实战

1. 基础异常响应封装
public record ErrorResponse(
Instant timestamp,
int status,
String error,
String message,
String path
) {
public ErrorResponse(HttpStatus status, String message, String path) {
this(Instant.now(), status.value(), status.getReasonPhrase(), message, path);
}
}
2. 核心异常处理器实现
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity handleBusinessException(
BusinessException ex, WebRequest request) {
return createResponse(HttpStatus.BAD_REQUEST, ex, request);
}
// 处理数据不存在异常
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity handleEntityNotFound(
EntityNotFoundException ex, WebRequest request) {
return createResponse(HttpStatus.NOT_FOUND, ex, request);
}
// 处理所有未捕获异常
@ExceptionHandler(Exception.class)
public ResponseEntity handleAllExceptions(
Exception ex, WebRequest request) {
return createResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex, request);
}
// 统一响应构建方法
private ResponseEntity createResponse(
HttpStatus status, Exception ex, WebRequest request) {
String path = ((ServletWebRequest) request).getRequest().getRequestURI();
ErrorResponse body = new ErrorResponse(status, ex.getMessage(), path);
return ResponseEntity.status(status).body(body);
}
}
3. 自定义业务异常
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}

四、深度解析异常处理流程

1. 异常处理调用链

2. 异常匹配优先级

五、高级应用场景

1. 处理验证异常(Spring Validation)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
String fieldName = error.getField();
String message = error.getDefaultMessage();
errors.put(fieldName, message);
});
return new ErrorResponse(
HttpStatus.BAD_REQUEST,
"验证失败",
errors
);
}
2. 处理安全异常(Spring Security)
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity handleAccessDenied(
AccessDeniedException ex, WebRequest request) {
String path = ((ServletWebRequest) request).getRequest().getRequestURI();
ErrorResponse body = new ErrorResponse(
HttpStatus.FORBIDDEN,
"无权访问此资源",
path
);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(body);
}

六、性能优化最佳实践

  1. 异常分类处理:精确匹配特定异常,避免使用过于宽泛的Exception.class

  2. 层次化异常结构

    // 基础业务异常
    public abstract class BaseException extends RuntimeException {
    private final ErrorCode errorCode;
    public BaseException(ErrorCode errorCode, String message) {
    super(message);
    this.errorCode = errorCode;
    }
    }
    // 具体业务异常
    public class PaymentException extends BaseException {
    public PaymentException(String message) {
    super(ErrorCode.PAYMENT_FAILED, message);
    }
    }

  3. 异常日志分级

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity handleBusinessException(
    BusinessException ex, WebRequest request) {
    // 业务异常只记录DEBUG日志
    log.debug("Business exception occurred: {}", ex.getMessage());
    return createResponse(HttpStatus.BAD_REQUEST, ex, request);
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity handleAllExceptions(
    Exception ex, WebRequest request) {
    // 未知异常记录ERROR日志
    log.error("Unexpected error occurred", ex);
    return createResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex, request);
    }

 七、实战

正常请求

GET /api/users/123
Status: 200 OK
Response: {"id":123,"name":"John Doe"}

异常请求

GET /api/users/999
Status: 404 Not Found
Response: {
"timestamp": "2023-08-20T10:15:30Z",
"status": 404,
"error": "Not Found",
"message": "用户不存在",
"path": "/api/users/999"
}

验证失败请求

POST /api/users
Payload: {"name": "", "email": "invalid-email"}
Status: 400 Bad Request
Response: {
"timestamp": "2023-08-20T10:18:45Z",
"status": 400,
"error": "Bad Request",
"message": "验证失败",
"errors": {
"name": "姓名不能为空",
"email": "邮箱格式不正确"
}
}

八、总结

1、@RestControllerAdvice = @ControllerAdvice + @ResponseBody

  • 全局处理控制器异常
  • 响应体自动序列化(JSON/XML)

2、异常处理优先级

  • Controller局部处理器 > 全局处理器
  • 具体异常 > 通用异常

3.最佳实践

// 返回明确HTTP状态码
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(UserNotFoundException.class)
// 使用ResponseEntity精确控制响应
@ExceptionHandler(PaymentException.class)
public ResponseEntity handlePaymentException() {
// 自定义响应头等复杂操作
}

4、生产环境建议

  • 禁用敏感错误信息(spring.mvc.include-error-details)
  • 自定义错误码体系
  • 异常国际化支持

So, 当再次处理错误时,应该能独挡一面了。当然,业务中,异常还应该有日志记录,如ES,traceInfo, 上下文等信息,便于排错.

posted @ 2025-07-25 11:30  yfceshi  阅读(40)  评论(0)    收藏  举报