completablefuture通过exceptionally、handle、whencomplete实现异步异常分支处理:exceptionally提供默认值并保持类型一致;handle统一处理成功与失败且支持类型转换;whencomplete仅做副作用不改变结果;可组合策略应对复杂场景。

CompletableFuture 提供了清晰、非阻塞的异常分支处理能力,关键不在于“捕获后吞掉异常”,而是在异步链中主动定义失败路径,让程序在出错时仍能返回有意义的结果或执行补偿逻辑。
用 exceptionally 提供默认值
这是最直接的异常分支写法:仅当上游任务抛出异常时触发,返回替代结果,后续阶段照常执行。
- 适用于网络超时、空数据、权限拒绝等可降级场景
- 函数参数是 Throwable,可判断异常类型做差异化处理
- 返回值类型必须与上游 CompletableFuture 的泛型一致
示例:
if (Math.random() return "原始结果";
}).exceptionally(ex -> {
if (ex instanceof IOException) return "离线缓存内容";
else return "系统繁忙,请稍后再试";
});
用 handle 统一收口成功与失败
handle 接收两个参数:正常结果(可能为 null)和异常(可能为 null),二者必居其一。它总被执行,适合做结果归一化、日志埋点或兜底转换。
- 返回值类型可与上游不同(支持泛型转换)
- 避免重复写 success/fail 分支逻辑,代码更集中
- 注意:若上游异常未被 handle 捕获,异常会继续向下游传播
示例:
CompletableFuture.handle((data, ex) -> {
if (ex != null) {
log.error("调用失败", ex);
return "fallback_" + System.currentTimeMillis();
} else {
return "success_" + data.length();
}
});
用 whenComplete 做副作用处理
whenComplete 只负责“观察”结果或异常,不改变返回值,典型用于清理资源、打监控指标、发告警,但不影响主流程走向。
- 参数是 BiConsumer
,无返回值 - 即使上游异常,它也会执行;但不会阻止异常向下游传递
- 若需同时记录日志+提供默认值,应搭配 exceptionally 使用
示例:
CompletableFuture.whenComplete((order, ex) -> {
if (ex != null) {
metrics.counter("order_create_fail").increment();
} else {
metrics.timer("order_create_success").record(System.nanoTime() - start);
}
})
.exceptionally(ex -> fallbackOrder());
组合多个异常策略应对复杂场景
真实业务中,异常分支往往不是单一层级。比如先重试,再降级,最后告警——可通过 thenCompose 链式编排实现。
- 在 exceptionally 中返回另一个 CompletableFuture,实现“异常后发起新异步任务”
- 配合 orTimeout 可设置整体超时,避免无限等待
- 慎用 obtrudeValue:它强行覆盖结果,会破坏状态一致性,仅限测试或极端兜底
示例(失败后重试一次):
CompletableFuture.exceptionally(ex -> CompletableFuture.supplyAsync(() -> {
log.warn("首次调用失败,启动重试");
return callExternalService(); // 重试逻辑
}).join()) // 注意:此处 join 是为保持返回类型一致,生产环境建议用 thenCompose
.orTimeout(5, TimeUnit.SECONDS);
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











