面试题答案
一键面试异常处理机制对数据流后续处理流程的影响
onErrorResume
:- 当数据流中出现异常时,
onErrorResume
会捕获异常,并使用一个备用的Publisher
来继续数据流。这意味着数据流不会终止,而是从异常点切换到备用的数据流。例如:
Flux.just(1, 2, 3) .map(i -> { if (i == 2) { throw new RuntimeException("Simulated error"); } return i * 2; }) .onErrorResume(e -> Flux.just(4, 5, 6)) .subscribe(System.out::println);
- 这里当
i == 2
抛出异常时,onErrorResume
捕获异常并开始发射4, 5, 6
,后续操作符会作用于这个新的数据流。
- 当数据流中出现异常时,
onErrorReturn
:onErrorReturn
会在捕获到异常时,返回一个固定的值来继续数据流。数据流同样不会终止。例如:
Flux.just(1, 2, 3) .map(i -> { if (i == 2) { throw new RuntimeException("Simulated error"); } return i * 2; }) .onErrorReturn(-1) .subscribe(System.out::println);
- 当
i == 2
抛出异常时,onErrorReturn
返回-1
,然后数据流继续,后续操作符会作用于这个-1
。
不同操作符组合场景下异常处理的最佳实践
map
和filter
操作符场景:- 最佳实践:在
map
和filter
操作符后尽早使用异常处理。因为这些操作符通常用于对单个元素进行转换或过滤,如果在这些操作中出现异常,使用onErrorResume
或onErrorReturn
可以保证数据流继续,避免整个数据流因单个元素的问题而终止。例如:
Flux.just(1, 2, 3) .map(i -> { if (i == 2) { throw new RuntimeException("Simulated error in map"); } return i * 2; }) .filter(j -> j > 2) .onErrorResume(e -> Flux.just(4, 5, 6)) .subscribe(System.out::println);
- 这样即使
map
操作中出现异常,数据流仍能继续处理。
- 最佳实践:在
flatMap
操作符场景:- 最佳实践:如果
flatMap
内部发射的Publisher
可能抛出异常,可以在flatMap
内部的Publisher
上使用异常处理,或者在flatMap
之后使用全局的异常处理。例如:
Flux.just(1, 2, 3) .flatMap(i -> { if (i == 2) { return Flux.error(new RuntimeException("Simulated error in flatMap")); } return Flux.just(i * 2); }) .onErrorResume(e -> Flux.just(4, 5, 6)) .subscribe(System.out::println);
- 这里在
flatMap
之后处理异常。也可以在flatMap
内部的Publisher
上处理异常,比如Flux.error(e).onErrorReturn(-1)
,这样可以根据具体需求灵活处理异常,保证数据流的稳定性。
- 最佳实践:如果
- 复杂操作符组合场景:
- 最佳实践:在复杂操作符组合中,异常处理的位置要根据业务需求来定。如果希望某个局部操作的异常不影响整个数据流,可以在该局部操作附近处理异常;如果希望统一处理整个数据流的异常,可以在数据流的末尾使用异常处理。例如,在包含多个
map
、filter
、flatMap
的组合中,如果某些map
操作是相互依赖的,那么在最后一个依赖的map
操作后处理异常,可能比在每个map
操作后都处理异常更合适,这样可以避免不必要的备用数据流切换,保持数据流处理逻辑的清晰。同时,要考虑异常处理对性能的影响,过多的异常处理可能会增加代码的复杂性和性能开销。
- 最佳实践:在复杂操作符组合中,异常处理的位置要根据业务需求来定。如果希望某个局部操作的异常不影响整个数据流,可以在该局部操作附近处理异常;如果希望统一处理整个数据流的异常,可以在数据流的末尾使用异常处理。例如,在包含多个