如何修改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
相关产品推荐
相关产品推荐

