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

如何修改CompletableFuture代码,使exceptionally区分抛出两种异常?

问题:如何让CompletableFuture在原异常为ServiceWarningException时抛出该异常?

现有createCode方法:

private CompletableFuture<String> createCode(CodeRequest codeRequest) {
    return salesCompletableFuture
        .supplyAsync(() -> getVoucherCode(salesRequest), forkJoinPool.threadPool())
        .exceptionally(ServiceException::handleFutureException);
}

对应的handleFutureException方法实现:

public static <T> T handleFutureException(Throwable ex) {
    if (ex instanceof CompletionException || ex instanceof ExecutionException) {
      if (ex.getCause() instanceof ServiceException) {
        throw new ServiceException((ServiceException) ex.getCause());
      } else {
        throw new ServiceException(ex.getCause().getMessage(), ex.getCause());
      }
    }
    else if (ex instanceof ServiceException exception) {
      throw new ServiceException(exception);
    }
    throw new ServiceException(ex.getMessage(), ex);
  }

  public ServiceException(String message, Throwable cause) {
    super(message, cause);
  }
}

当前这个CompletableFuture只会抛出ServiceException,我想修改代码,让原异常是ServiceWarningException时直接抛出该异常,其他情况仍抛出ServiceException。之前我在handleFutureException里加了如下分支,但没生效:

else if (ex instanceof ServiceWarningException exception) {
      throw new ServiceWarningException(exception);
}

原因分析

你加的分支没生效,核心问题是:CompletableFuture的supplyAsync里抛出的异常会被包装成CompletionException(或者ExecutionException),此时ex是外层的包装异常,而ServiceWarningException是在ex.getCause()里,你的分支判断的是外层的ex,自然不会触发。

修改方案

需要调整异常处理逻辑,先处理被包装的异常,在获取cause后优先判断是否为ServiceWarningException;同时也要处理未被包装的直接抛出的ServiceWarningException。修改后的handleFutureException方法如下:

public static <T> T handleFutureException(Throwable ex) {
    // 先处理被CompletionException/ExecutionException包裹的异常
    if (ex instanceof CompletionException || ex instanceof ExecutionException) {
        Throwable cause = ex.getCause();
        if (cause instanceof ServiceWarningException warningEx) {
            throw new ServiceWarningException(warningEx);
        } else if (cause instanceof ServiceException serviceEx) {
            throw new ServiceException(serviceEx);
        } else {
            throw new ServiceException(cause.getMessage(), cause);
        }
    }
    // 处理未被包裹的直接抛出的异常
    else if (ex instanceof ServiceWarningException warningEx) {
        throw new ServiceWarningException(warningEx);
    }
    else if (ex instanceof ServiceException serviceEx) {
        throw new ServiceException(serviceEx);
    }
    // 其他异常统一包装成ServiceException
    throw new ServiceException(ex.getMessage(), ex);
}

// 确保ServiceWarningException有对应的拷贝构造方法
public ServiceWarningException(ServiceWarningException ex) {
    super(ex.getMessage(), ex.getCause());
    // 如果有自定义字段,也要一并拷贝
}

关键调整点:

  • 在处理包裹异常的分支里,先判断cause是否为ServiceWarningException,优先抛出该异常
  • 把未被包裹的ServiceWarningException判断分支移到ServiceException分支之前,确保优先匹配
  • 要保证ServiceWarningException有对应的拷贝构造方法,否则无法正确实例化抛出

这样修改后,当原异常是ServiceWarningException时,无论是被包裹还是直接抛出,都会触发对应的分支,抛出ServiceWarningException;其他情况则继续抛出ServiceException。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:10:03