CompletionStage 的异常处理

CompletionStage 的异常处理

1. 异常处理流程

flowchart TD A[Stage1: 异步任务] -->|正常完成| B[Stage2: 处理结果] A -->|抛出异常| C[Stage2: 捕获异常] C -->|whenComplete| D[记录日志/清理资源\n不改变结果] C -->|handle| E[返回替代结果\n覆盖异常] D -->|继续传递异常| F[后续阶段仍收到异常] E -->|替换为正常结果| G[后续阶段使用新值]

2. 关键方法对比

方法 作用 是否改变结果 示例
whenComplete 记录日志,但保留原结果/异常 ❌ 不改变 stage.whenComplete((res, ex) -> log(ex))
handle 处理异常并返回替代结果 ✔️ 改变 stage.handle((res, ex) -> ex != null ? 0 : res)
exceptionally 仅处理异常,返回默认值 ✔️ 改变 stage.exceptionally(ex -> 0)

3. 分步骤详解(含代码)

场景模拟

假设有一个异步任务可能成功或失败:

CompletableFuture<Integer> stage1 = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        return 10; // 正常完成
    } else {
        throw new RuntimeException("Oops!"); // 故意抛出异常
    }
});

方式1:未处理异常(直接崩溃)

stage1
    .thenApply(x -> x * 2) // 若 stage1 异常,此处不执行
    .thenAccept(System.out::println);
  • 流程图
    flowchart LR A[Stage1 异常] --> B[thenApply 被跳过] B --> C[thenAccept 被跳过] C --> D[整个链异常结束]
  • 结果:整个调用链因异常终止,无输出。

方式2:使用 whenComplete(记录异常,但结果不变)

stage1
    .whenComplete((result, ex) -> {
        if (ex != null) {
            System.out.println("Stage1 异常: " + ex.getMessage());
        }
    })
    .thenApply(x -> x * 2) // 若 stage1 异常,此处接收 null 并抛出 NPE
    .thenAccept(System.out::println);
  • 流程图
    flowchart LR A[Stage1 异常] --> B[whenComplete 记录日志] B -->|传递原异常| C[thenApply 接收 null → NPE] C --> D[整个链再次异常]
  • 结果
    Stage1 异常: Oops!
    抛出 NullPointerException(因为 thenApply 接收 null)
    

方式3:使用 handle(替换异常为默认值)

stage1
    .handle((result, ex) -> {
        if (ex != null) {
            System.out.println("Stage1 异常,返回 0");
            return 0; // 替换异常为默认值
        }
        return result;
    })
    .thenApply(x -> x * 2) // 0 → 0
    .thenAccept(System.out::println); // 输出 0
  • 流程图
    flowchart LR A[Stage1 异常] --> B[handle 返回 0] B -->|传递新值| C[thenApply 0 → 0] C --> D[输出 0]
  • 结果
    Stage1 异常,返回 0
    0
    

方式4:使用 exceptionally(仅处理异常)

stage1
    .exceptionally(ex -> {
        System.out.println("Stage1 异常,返回 -1");
        return -1;
    })
    .thenApply(x -> x * 2) // -1 → -2
    .thenAccept(System.out::println); // 输出 -2
  • 流程图
    flowchart LR A[Stage1 异常] --> B[exceptionally 返回 -1] B --> C[thenApply -1 → -2] C --> D[输出 -2]
  • 结果
    Stage1 异常,返回 -1
    -2
    

4. 对比总结

场景 whenComplete handle exceptionally
是否改变结果 不改变(只记录) 可返回新值覆盖异常 仅异常时返回默认值
后续阶段接收内容 原结果或异常 新结果或异常 新结果或异常
典型用途 日志记录、资源清理 异常恢复 简化异常处理

5. 最佳实践

  1. 明确处理边界

    • 在链式调用中尽早处理异常(如 handleexceptionally),避免异常扩散。
    stage1
        .handle(...) // 第一个处理点
        .thenApply(...)
        .thenAccept(...);
    
  2. 区分 whenComplete 和 handle

    • 需要 只记录不修复 时用 whenComplete
    • 需要 修复异常并继续 时用 handle
  3. 避免 NPE

    • 如果使用 whenComplete,确保后续阶段能处理可能的 null 值。

最终结论
CompletionStage 的异常处理像一个「管道中的错误传递」——如果不主动处理,异常会一直传递到链的末端;而通过 handle/exceptionally 可以像「阀门」一样截断异常,替换为正常值继续流动。

posted @ 2025-03-25 23:18  皮皮是个不挑食的好孩子  阅读(46)  评论(0)    收藏  举报