mermaid
graph TD
A[客户端请求] --> B[DispatcherServlet]
B --> C{Controller处理}
C -->|正常| D[返回数据]
C -->|异常| E[ExceptionHandlerResolver]
E --> F[查找@ExceptionHandler]
F --> G[执行全局异常处理器]
G --> H[返回统一错误响应]
组件 | 作用范围 | 优先级 | 适用场景 |
---|---|---|---|
@ExceptionHandler | 单个Controller | 高 | 控制器特定异常处理 |
@ControllerAdvice | 全局范围 | 中 | 统一异常处理架构 |
HandlerExceptionResolver | 全局 | 低 | 底层异常处理扩展 |
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseResult handleException(Exception e) {
return ResponseResult.fail(500, "系统繁忙: " + e.getMessage());
}
}
java
@Data
@Schema(description = "统一响应结构")
public class ResponseResult {
@Schema(description = "状态码", example = "200")
private int code;
@Schema(description = "业务数据")
private T data;
@Schema(description = "提示信息", example = "操作成功")
private String message;
public static ResponseResult success(T data) {
return new ResponseResult<>(200, data, "success");
}
public static ResponseResult fail(int code, String msg) {
return new ResponseResult<>(code, null, msg);
}
}
java
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult handleValidationException(MethodArgumentNotValidException ex) {
List errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toList());
return ResponseResult.fail(400001, "参数校验失败: " + String.join("; ", errors));
}
java
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
@ExceptionHandler(BusinessException.class)
public ResponseResult handleBusinessException(BusinessException e) {
return ResponseResult.fail(e.getErrorCode().getCode(), e.getMessage());
}
java
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(HttpStatus.CONFLICT)
public ResponseResult handleDataIntegrityViolation(DataIntegrityViolationException ex) {
String message = "数据库操作异常";
if (ex.getCause() instanceof ConstraintViolationException) {
message = "数据完整性约束违反: " + ex.getCause().getMessage();
}
return ResponseResult.fail(409001, message);
}
java
@Operation(responses = {
@ApiResponse(responseCode = "200", description = "成功",
content = @Content(schema = @Schema(implementation = UserVO.class))),
@ApiResponse(responseCode = "400", description = "参数错误",
content = @Content(schema = @Schema(implementation = ErrorResult.class))),
@ApiResponse(responseCode = "500", description = "系统错误",
content = @Content(schema = @Schema(implementation = ErrorResult.class)))
})
@GetMapping("/{id}")
public ResponseResult getUser(@PathVariable Long id) {
// ...
}
java
@Schema(name = "ErrorResult", description = "错误详情")
public class ErrorResult {
@Schema(description = "错误时间", example = "2024-03-15 10:00:00")
private String timestamp;
@Schema(description = "错误路径", example = "/api/users/123")
private String path;
@Schema(description = "错误详情")
private Map details;
}
java
// 优先级从上到下依次降低
@ExceptionHandler({
MethodArgumentNotValidException.class, // 参数校验
HttpRequestMethodNotSupportedException.class, // 方法不支持
BusinessException.class, // 业务异常
AccessDeniedException.class, // 权限异常
Exception.class // 兜底处理
})
java
@ExceptionHandler(Exception.class)
public ResponseResult handleGeneralException(Exception ex, HttpServletRequest request) {
ErrorResult errorResult = new ErrorResult();
errorResult.setPath(request.getRequestURI());
if (environment.acceptsProfiles(Profiles.of("prod"))) {
errorResult.setDetails(Collections.singletonMap("error", "系统繁忙"));
} else {
errorResult.setDetails(new HashMap(){{
put("exception", ex.getClass().getName());
put("message", ex.getMessage());
put("stackTrace", Arrays.stream(ex.getStackTrace())
.limit(5)
.map(StackTraceElement::toString)
.collect(Collectors.toList()));
}});
}
return ResponseResult.fail(500, errorResult);
}
java
@ExceptionHandler(Exception.class)
public ResponseResult handleException(Exception ex, HttpServletRequest request) {
// 记录异常到监控系统
monitorService.recordException(ex, request);
// 发送告警通知
if (ex instanceof CriticalException) {
alertService.sendCriticalAlert(ex);
}
return buildErrorResponse(ex, request);
}
java
// 主处理器(处理公共异常)
@RestControllerAdvice
public class MainExceptionHandler {
// 基础异常处理
}
// 业务模块专用处理器
@RestControllerAdvice(assignableTypes = OrderController.class)
public class OrderExceptionHandler {
@ExceptionHandler(OrderNotFoundException.class)
public ResponseResult handleOrderNotFound(OrderNotFoundException ex) {
return ResponseResult.fail(404001, ex.getMessage());
}
}
java
@ExceptionHandler(BusinessException.class)
public ResponseResult handleBusinessException(BusinessException ex,
Locale locale) {
String message = messageSource.getMessage(ex.getErrorCode().name(),
ex.getParams(), locale);
return ResponseResult.fail(ex.getErrorCode().getCode(), message);
}
java
@RestControllerAdvice
@Async
public class AsyncExceptionHandler {
@ExceptionHandler(AsyncTaskException.class)
public CompletableFuture> handleAsyncException(AsyncTaskException ex) {
return CompletableFuture.completedFuture(
ResponseResult.fail(500100, "异步任务异常: " + ex.getMessage())
);
}
}
异常未捕获
✅ 检查ControllerAdvice包扫描范围
✅ 确认没有更高优先级的局部@ExceptionHandler
Swagger文档缺失错误响应
✅ 使用@ApiResponse显式声明响应类型
✅ 配置GenericResponseService
循环依赖问题
✅ 避免在异常处理器中注入复杂服务
✅ 使用@Lazy延迟加载依赖
properties
# 开启详细错误日志
logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.boot.autoconfigure=TRACE
# 禁用白标错误页面
server.error.whitelabel.enabled=false
java
@Bean
public static BeanPostProcessor exceptionSanitizer() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof DefaultErrorAttributes) {
((DefaultErrorAttributes) bean).setErrorAttributeOptions(
ErrorAttributeOptions.of(
ErrorAttributeOptions.Include.EXCEPTION,
ErrorAttributeOptions.Include.MESSAGE,
ErrorAttributeOptions.Include.BINDING_ERRORS
)
);
}
return bean;
}
};
}
java
@ExceptionHandler({SQLException.class, DataAccessException.class})
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseResult handleDbException(RuntimeException ex) {
String errorKey = DigestUtils.md5DigestAsHex(ex.getMessage().getBytes());
if (exceptionCache.getIfPresent(errorKey) == null) {
exceptionCache.put(errorKey, true);
alertService.sendDatabaseAlert(ex);
}
return ResponseResult.fail(503001, "数据库服务异常");
}
任务1:构建分级异常处理体系
java
// 定义异常优先级处理顺序
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class PrimaryExceptionHandler {
// 处理核心业务异常
}
@Order(Ordered.LOWEST_PRECEDENCE)
@RestControllerAdvice
public class FallbackExceptionHandler {
// 兜底异常处理
}
任务2:实现异常触发熔断机制
java
@ExceptionHandler(CircuitBreakerOpenException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseResult handleCircuitBreakerOpen() {
return ResponseResult.fail(503002, "服务暂时不可用,请稍后重试");
}
如何平衡异常处理的灵活性与统一性?
微服务架构下的异常传播
java
@ExceptionHandler(FeignException.class)
public ResponseResult handleFeignException(FeignException e,
HttpServletRequest request) {
String traceId = request.getHeader("X-B3-TraceId");
return ResponseResult.fail(502001, "下游服务调用失败["+traceId+"]");
}
通过本文你将掌握:
✅ 全局异常处理核心原理与实现
✅ 生产环境异常处理最佳实践
✅ Swagger异常文档集成技巧
✅ 复杂业务场景的异常处理方案
讨论话题:
在微服务架构中如何设计跨服务的异常处理机制?如何处理分布式事务中的异常传播与补偿?