You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义ExceptionHandler导致Resilience4j的@CircuitBreaker与@Retry不触发

解决@CircuitBreaker/@Retry与自定义响应模型的冲突问题

方案1:业务层主动抛出异常,异常处理器统一转换响应

既然@CircuitBreaker和@Retry需要异常触发,那就不在业务逻辑层直接返回自定义响应实体,而是在业务执行失败时抛出特定业务异常,让容错机制先捕获异常执行逻辑,再由CustomExceptionHandler把异常转换成自定义响应返回给前端。

示例代码:

1.1 定义业务异常

public class ServiceCallFailedException extends RuntimeException {
    public ServiceCallFailedException(String message) {
        super(message);
    }
}

1.2 业务方法抛出异常(配合容错注解)

@Service
public class RemoteService {
    @CircuitBreaker(name = "remoteService", fallbackMethod = "fallbackCall")
    @Retry(name = "remoteService", retryExceptions = ServiceCallFailedException.class)
    public String callThirdParty() {
        // 模拟第三方服务调用失败
        boolean callFailed = true;
        if (callFailed) {
            throw new ServiceCallFailedException("第三方服务调用超时");
        }
        return "正常响应数据";
    }

    public String fallbackCall(Throwable throwable) {
        return "熔断降级返回数据";
    }
}

1.3 异常处理器转换响应

@ControllerAdvice
public class CustomExceptionHandler {
    @ExceptionHandler(ServiceCallFailedException.class)
    public ResponseEntity<CustomResponse> handleServiceException(ServiceCallFailedException e) {
        CustomResponse response = new CustomResponse();
        response.setCode(500);
        response.setMessage(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

方案2:基于响应结果配置容错触发条件(无需抛出异常)

如果不想改动现有业务逻辑的返回结构,可以利用Resilience4j(主流容错框架)的自定义断言,让熔断/重试逻辑根据自定义响应实体的状态来触发,而非依赖异常。

示例:配置CircuitBreaker根据响应结果判断是否触发熔断

@Service
public class RemoteService {
    @CircuitBreaker(
        name = "remoteService",
        fallbackMethod = "fallbackCall",
        circuitBreakerConfig = @CircuitBreakerConfig(
            predicate = "responsePredicate"
        )
    )
    public CustomResponse callThirdParty() {
        // 直接返回自定义响应,比如失败状态的响应
        CustomResponse failedResponse = new CustomResponse();
        failedResponse.setCode(500);
        failedResponse.setMessage("服务调用失败");
        return failedResponse;
    }

    public CustomResponse fallbackCall(CustomResponse response, Throwable throwable) {
        CustomResponse fallbackResponse = new CustomResponse();
        fallbackResponse.setCode(200);
        fallbackResponse.setMessage("降级返回数据");
        return fallbackResponse;
    }

    // 自定义断言:判断响应是否为失败状态
    public static Predicate<CustomResponse> responsePredicate() {
        return response -> response.getCode() == 500;
    }
}

Retry同理,可以配置retryOnResult属性,指定判断响应是否需要重试的逻辑。

方案3:缩小异常处理器的拦截范围

调整CustomExceptionHandler的拦截范围,只处理Controller层抛出的异常,让业务层的异常(或响应中的错误状态)先被容错机制捕获。

比如限制异常处理器只处理当前Controller包下的类:

@ControllerAdvice(basePackages = "com.yourproject.controller")
public class CustomExceptionHandler {
    // 仅处理Controller层抛出的异常,业务层异常留给容错机制处理
    @ExceptionHandler(Exception.class)
    public ResponseEntity<CustomResponse> handleControllerExceptions(Exception e) {
        // 转换为自定义响应
        CustomResponse response = new CustomResponse();
        response.setCode(500);
        response.setMessage(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

这样业务层的异常不会被提前拦截,@CircuitBreaker和@Retry能正常捕获并执行逻辑,最终异常还是会被Controller层抛出后由处理器转换为自定义响应。


内容的提问来源于stack exchange,提问作者Julio Vargas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:42:40