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<ErrorResponse> handleBusinessException(BusinessException ex, WebRequest request) {return createResponse(HttpStatus.BAD_REQUEST, ex, request);}// 处理数据不存在异常@ExceptionHandler(EntityNotFoundException.class)public ResponseEntity<ErrorResponse> handleEntityNotFound(EntityNotFoundException ex, WebRequest request) {return createResponse(HttpStatus.NOT_FOUND, ex, request);}// 处理所有未捕获异常@ExceptionHandler(Exception.class)public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, WebRequest request) {return createResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex, request);}// 统一响应构建方法private ResponseEntity<ErrorResponse> 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<String, String> 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<ErrorResponse> 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);
}
六、性能优化最佳实践
异常分类处理:精确匹配特定异常,避免使用过于宽泛的Exception.class
层次化异常结构:
// 基础业务异常 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);} }
异常日志分级:
@ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse> 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<ErrorResponse> 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<ErrorResponse> handlePaymentException() {// 自定义响应头等复杂操作
}
4、生产环境建议:
- 禁用敏感错误信息(spring.mvc.include-error-details)
- 自定义错误码体系
- 异常国际化支持
So, 当再次处理错误时,应该能独挡一面了。当然,业务中,异常还应该有日志记录,如ES,traceInfo, 上下文等信息,便于排错.