一、引言:为什么需要全局异常处理?
在RESTful API开发中,优雅且一致的错误处理机制至关重要。传统方式在每个Controller中单独处理异常会导致大量重复代码,且难以维护。Spring框架提供的@RestControllerAdvice
注解完美解决了这个问题,它实现了AOP思想,将横切关注点(如异常处理)从业务逻辑中分离出来。
二、@RestControllerAdvice
核心解析
2.1 基本概念
@RestControllerAdvice
是@ControllerAdvice
和@ResponseBody
的组合注解:
@ControllerAdvice
:声明一个全局处理器@ResponseBody
:自动将返回值序列化为JSON/XML
2.2 底层实现原理
Spring通过DispatcherServlet
的processDispatchResult()
方法处理异常,当控制器抛出异常时,会遍历所有@ControllerAdvice
中的@ExceptionHandler
方法寻找匹配处理器。
// 伪代码展示Spring处理流程
try {
handlerAdapter.handle(processedRequest, response, handler);
} catch (Exception ex) {
// 查找@ExceptionHandler
ModelAndView mav = exceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
render(mav, request, response);
}
}
三、完整代码示例与深度解析
3.1 基础异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException ex) {
log.warn("业务异常: {}", ex.getMessage());
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.fail(ex.getErrorCode(), ex.getMessage()));
}
/**
* 处理参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = ex.getBindingResult().getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
fieldError -> Optional.ofNullable(fieldError.getDefaultMessage()).orElse("")
));
return ResponseEntity
.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(ApiResponse.fail(ErrorCode.INVALID_PARAM, "参数校验失败", errors));
}
/**
* 处理所有未明确捕获的异常
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleAllExceptions(Exception ex) {
log.error("系统异常: ", ex);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.fail(ErrorCode.SYSTEM_ERROR, "系统繁忙,请稍后再试"));
}
}
3.2 响应体结构设计
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApiResponse<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
private T data;
private Long timestamp = System.currentTimeMillis();
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "成功", data, System.currentTimeMillis());
}
public static <T> ApiResponse<T> fail(int code, String message) {
return new ApiResponse<>(code, message, null, System.currentTimeMillis());
}
public static <T> ApiResponse<T> fail(int code, String message, T data) {
return new ApiResponse<>(code, message, data, System.currentTimeMillis());
}
}
3.3 自定义业务异常
public class BusinessException extends RuntimeException {
private final int errorCode;
public BusinessException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
四、高级特性与最佳实践
4.1 细粒度控制
// 只处理特定包下的控制器
@RestControllerAdvice(basePackages = "com.example.api.v1")
// 只处理带有特定注解的控制器
@RestControllerAdvice(annotations = RestController.class)
// 排除特定控制器
@RestControllerAdvice(assignableTypes = {BaseController.class})
4.2 响应状态码控制
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ApiResponse<Void> handleResourceNotFound(ResourceNotFoundException ex) {
return ApiResponse.fail(404, ex.getMessage());
}
4.3 国际化支持
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Void>> handleValidationException(
MethodArgumentNotValidException ex,
Locale locale) {
String errorMessage = messageSource.getMessage(
"validation.error",
null,
locale);
// 其他处理逻辑...
}
五、性能优化建议
- 异常匹配缓存:Spring会缓存异常与处理方法的映射关系,不必担心反射性能问题
- 避免过度捕获:不要滥用
Exception.class
的全局捕获 - 日志分级:
- 业务异常使用WARN级别
- 系统异常使用ERROR级别
- 异步处理:对于需要复杂处理的异常,可以结合
@Async
@ExceptionHandler(ComplexException.class)
public CompletableFuture<ApiResponse<Void>> handleComplexException(ComplexException ex) {
return CompletableFuture.supplyAsync(() -> {
// 复杂处理逻辑
return ApiResponse.fail(500, "处理完成");
});
}
六、常见问题解决方案
6.1 处理器不生效排查步骤
- 确认类被Spring管理(有
@Component
派生注解) - 检查包扫描范围是否包含该处理器
- 确认没有更具体的处理器优先匹配
- 检查过滤器是否已经处理了响应(如
Filter
中直接写回了响应)
6.2 处理顺序控制
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
@RestControllerAdvice
public class HighPriorityExceptionHandler {
// 处理特殊异常
}
@Order(Ordered.LOWEST_PRECEDENCE) // 最低优先级
@RestControllerAdvice
public class DefaultExceptionHandler {
// 处理通用异常
}
七、生产环境完整案例
7.1 安全异常处理
@RestControllerAdvice
public class SecurityExceptionHandler {
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public ApiResponse<Void> handleAccessDenied(AccessDeniedException ex) {
return ApiResponse.fail(403, "无权访问该资源");
}
@ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ApiResponse<Void> handleAuthentication(AuthenticationException ex) {
return ApiResponse.fail(401, "认证失败", null);
}
}
7.2 限流异常处理
@RestControllerAdvice
public class RateLimitExceptionHandler {
@ExceptionHandler(RateLimiterException.class)
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
public ApiResponse<Void> handleRateLimit(RateLimiterException ex) {
return ApiResponse.fail(429,
"请求过于频繁,请稍后再试",
Map.of("retryAfter", ex.getRetryAfterSeconds()));
}
}
八、总结
@RestControllerAdvice
是Spring Boot中实现统一异常处理的利器,通过合理设计可以实现:
- 业务异常与系统异常分离
- 统一的API响应格式
- 细粒度的异常处理策略
- 良好的国际化支持
关键实践要点:
- 定义清晰的异常层级结构
- 保持异常处理代码简洁
- 合理使用日志记录
- 为不同模块定制专属处理器
通过本文的深度解析和完整代码示例,开发者可以构建出健壮、可维护的全局异常处理体系,显著提升API的可靠性和开发者体验。