全局异常处理

Spring Boot 提供了 @RestControllerAdvice 注解和 @ExceptionHandler 注解,用于实现全局异常捕获和处理。前者用于开启全局的异常捕获,后者用于指定捕获哪些异常以及如何处理这些异常。

全局异常处理示意图

PIGX 全局异常处理

位置与作用范围

PIGX 提供的全局异常处理位于 pigx-common-sentinel 模块,通过条件注解限制其生效范围。

关键实现要点:

  1. 模块位置:全局异常处理类位于 pigx-common-sentinel 模块
  2. 条件限制@ConditionalOnExpression 注解限制了全局异常处理只对 OAuth 2.0 的资源服务器有效
  3. 异常捕获@ExceptionHandler 注解可以捕获具体的异常类型,进行相应的格式化处理
  4. 熔断指标:业务异常通过 Tracer.trace(e); 记录到 Sentinel,作为熔断降级的重要指标

实现示例

@Slf4j
@RestControllerAdvice
@ConditionalOnExpression("${security.oauth2.resource.server-enabled:true}")
public class GlobalExceptionHandler {

    /**
     * 全局异常处理
     * @param e 异常
     * @return 统一响应
     */
    @ExceptionHandler(Exception.class)
    public R handleException(Exception e) {
        log.error("全局异常信息:", e);
        return R.failed(e.getLocalizedMessage());
    }

    /**
     * 业务异常处理
     * @param e 业务异常
     * @return 统一响应
     */
    @ExceptionHandler(BusinessException.class)
    public R handleBusinessException(BusinessException e) {
        log.error("业务异常信息:", e);
        // 记录到 Sentinel,用于熔断降级
        Tracer.trace(e);
        return R.failed(e.getMessage());
    }

    /**
     * 参数校验异常处理
     * @param e 参数校验异常
     * @return 统一响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleValidationException(MethodArgumentNotValidException e) {
        log.error("参数校验异常:", e);
        BindingResult bindingResult = e.getBindingResult();
        String message = bindingResult.getAllErrors().get(0).getDefaultMessage();
        return R.failed(message);
    }

}
条件生效说明

全局异常处理通过 @ConditionalOnExpression 注解控制生效范围,仅在 OAuth 2.0 资源服务器模式下启用,避免影响其他类型的服务。

Sentinel 集成

业务异常必须通过 Tracer.trace(e) 方法记录到 Sentinel,这是实现服务熔断、降级等保护机制的重要指标来源。

自定义异常处理

如需自定义异常处理逻辑,可以在业务模块中创建新的 @RestControllerAdvice 类:

@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(CustomException.class)
    public R handleCustomException(CustomException e) {
        log.error("自定义异常:", e);
        Tracer.trace(e);
        return R.failed(e.getCode(), e.getMessage());
    }

}
最佳实践

建议为不同类型的异常提供专门的处理方法,返回统一的响应格式,便于前端统一处理错误信息。