2.5 Spring Boot异常处理全局化:@ControllerAdvice实战
Spring Boot全局异常处理:@ControllerAdvice深度解析
一、异常处理机制全景图
1.1 Spring MVC异常处理流程
mermaid
graph TD
    A[客户端请求] --> B[DispatcherServlet]
    B --> C{Controller处理}
    C -->|正常| D[返回数据]
    C -->|异常| E[ExceptionHandlerResolver]
    E --> F[查找@ExceptionHandler]
    F --> G[执行全局异常处理器]
    G --> H[返回统一错误响应]1.2 核心组件对比
| 组件 | 作用范围 | 优先级 | 适用场景 | 
|---|---|---|---|
| @ExceptionHandler | 单个Controller | 高 | 控制器特定异常处理 | 
| @ControllerAdvice | 全局范围 | 中 | 统一异常处理架构 | 
| HandlerExceptionResolver | 全局 | 低 | 底层异常处理扩展 | 
二、全局异常处理基础版
2.1 最小化配置示例
java
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseResult<Void> handleException(Exception e) {
        return ResponseResult.fail(500, "系统繁忙: " + e.getMessage());
    }
}2.2 统一响应模板
java
@Data
@Schema(description = "统一响应结构")
public class ResponseResult<T> {
    
    @Schema(description = "状态码", example = "200")
    private int code;
    
    @Schema(description = "业务数据")
    private T data;
    
    @Schema(description = "提示信息", example = "操作成功")
    private String message;
    public static <T> ResponseResult<T> success(T data) {
        return new ResponseResult<>(200, data, "success");
    }
    
    public static ResponseResult<Void> fail(int code, String msg) {
        return new ResponseResult<>(code, null, msg);
    }
}三、精细化异常处理策略
3.1 参数校验异常处理
java
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult<Void> handleValidationException(MethodArgumentNotValidException ex) {
    List<String> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
            .collect(Collectors.toList());
    return ResponseResult.fail(400001, "参数校验失败: " + String.join("; ", errors));
}3.2 自定义业务异常
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<Void> handleBusinessException(BusinessException e) {
    return ResponseResult.fail(e.getErrorCode().getCode(), e.getMessage());
}3.3 数据库异常处理
java
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(HttpStatus.CONFLICT)
public ResponseResult<Void> handleDataIntegrityViolation(DataIntegrityViolationException ex) {
    String message = "数据库操作异常";
    if (ex.getCause() instanceof ConstraintViolationException) {
        message = "数据完整性约束违反: " + ex.getCause().getMessage();
    }
    return ResponseResult.fail(409001, message);
}四、Swagger集成增强
4.1 异常响应文档化
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<UserVO> getUser(@PathVariable Long id) {
    // ...
}4.2 自定义错误Schema
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<String, Object> details;
}五、生产级最佳实践
5.1 异常分级处理策略
java
// 优先级从上到下依次降低
@ExceptionHandler({
    MethodArgumentNotValidException.class,   // 参数校验
    HttpRequestMethodNotSupportedException.class, // 方法不支持
    BusinessException.class,               // 业务异常
    AccessDeniedException.class,            // 权限异常
    Exception.class                         // 兜底处理
})5.2 敏感信息过滤
java
@ExceptionHandler(Exception.class)
public ResponseResult<Void> 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<String, Object>(){{
            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);
}5.3 异常监控集成
java
@ExceptionHandler(Exception.class)
public ResponseResult<Void> handleException(Exception ex, HttpServletRequest request) {
    // 记录异常到监控系统
    monitorService.recordException(ex, request);
    
    // 发送告警通知
    if (ex instanceof CriticalException) {
        alertService.sendCriticalAlert(ex);
    }
    
    return buildErrorResponse(ex, request);
}六、高级特性扩展
6.1 多异常处理器协同
java
// 主处理器(处理公共异常)
@RestControllerAdvice
public class MainExceptionHandler {
    // 基础异常处理
}
// 业务模块专用处理器
@RestControllerAdvice(assignableTypes = OrderController.class)
public class OrderExceptionHandler {
    @ExceptionHandler(OrderNotFoundException.class)
    public ResponseResult<Void> handleOrderNotFound(OrderNotFoundException ex) {
        return ResponseResult.fail(404001, ex.getMessage());
    }
}6.2 国际化异常消息
java
@ExceptionHandler(BusinessException.class)
public ResponseResult<Void> handleBusinessException(BusinessException ex, 
                                                   Locale locale) {
    String message = messageSource.getMessage(ex.getErrorCode().name(), 
                                             ex.getParams(), locale);
    return ResponseResult.fail(ex.getErrorCode().getCode(), message);
}6.3 异步异常处理
java
@RestControllerAdvice
@Async
public class AsyncExceptionHandler {
    
    @ExceptionHandler(AsyncTaskException.class)
    public CompletableFuture<ResponseResult<Void>> handleAsyncException(AsyncTaskException ex) {
        return CompletableFuture.completedFuture(
            ResponseResult.fail(500100, "异步任务异常: " + ex.getMessage())
        );
    }
}七、调试与问题排查
7.1 常见问题清单
-  异常未捕获 
 ✅ 检查ControllerAdvice包扫描范围
 ✅ 确认没有更高优先级的局部@ExceptionHandler
-  Swagger文档缺失错误响应 
 ✅ 使用@ApiResponse显式声明响应类型
 ✅ 配置GenericResponseService
-  循环依赖问题 
 ✅ 避免在异常处理器中注入复杂服务
 ✅ 使用@Lazy延迟加载依赖
7.2 调试技巧
properties
# 开启详细错误日志
logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.boot.autoconfigure=TRACE
# 禁用白标错误页面
server.error.whitelabel.enabled=false八、性能优化方案
8.1 异常堆栈优化
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;
        }
    };
}8.2 异常缓存机制
java
@ExceptionHandler({SQLException.class, DataAccessException.class})
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseResult<Void> 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<Void> handleCircuitBreakerOpen() {
    return ResponseResult.fail(503002, "服务暂时不可用,请稍后重试");
}扩展思考
-  如何平衡异常处理的灵活性与统一性? - 采用分层处理策略(全局处理基础异常,业务模块处理特有异常)
- 通过自定义异常代码体系实现错误分类
 
-  微服务架构下的异常传播 - 使用ErrorDecoder处理Feign异常
- 分布式追踪ID关联日志
 java @ExceptionHandler(FeignException.class) public ResponseResult<Void> handleFeignException(FeignException e, HttpServletRequest request) { String traceId = request.getHeader("X-B3-TraceId"); return ResponseResult.fail(502001, "下游服务调用失败["+traceId+"]"); }
通过本文你将掌握:
 ✅ 全局异常处理核心原理与实现
 ✅ 生产环境异常处理最佳实践
 ✅ Swagger异常文档集成技巧
 ✅ 复杂业务场景的异常处理方案
讨论话题:
 在微服务架构中如何设计跨服务的异常处理机制?如何处理分布式事务中的异常传播与补偿?
