如何将Mono.error存入Caffeine的AsyncLoadingCache?
问题描述
以下是相关代码示例:
AsyncLoadingCache<String, String> cache = Caffeine.newBuilder().buildAsync( (String k, Executor e) -> doThingReturnsMono() .subscribeOn(Schedulers.fromExecutor(e)) .toFuture() ); cache.put("fail", Mono.error<String>(new RuntimeException()).toFuture()); cache.put("success", Mono.just("success").toFuture()); System.err.println(cache.asMap().getKeys()); // Prints ["success"]
从输出可见,调用cache.put存入Mono.error转换的Future时,缓存仅保留了成功的键值对。请问如何实现将Mono.error存入AsyncLoadingCache?
解决方案
Caffeine的AsyncLoadingCache默认会自动丢弃最终失败的Future对应的条目,因为它的设计目标是缓存成功计算的结果。要存储失败状态,你需要把错误包装成合法的缓存值,而非直接传递失败的Future。
具体实现步骤
- 定义结果封装类:创建一个密封接口或类,用来同时承载成功值和错误信息,保证类型安全:
public sealed interface CacheResult<T> permits SuccessResult, ErrorResult { static <T> CacheResult<T> success(T value) { return new SuccessResult<>(value); } static <T> CacheResult<T> error(Throwable throwable) { return new ErrorResult<>(throwable); } } record SuccessResult<T>(T value) implements CacheResult<T> {} record ErrorResult<T>(Throwable throwable) implements CacheResult<T> {}
- 调整缓存泛型与操作逻辑:将缓存的泛型改为
CacheResult<String>,在Mono流中统一包装结果或错误:
AsyncLoadingCache<String, CacheResult<String>> cache = Caffeine.newBuilder().buildAsync( (String k, Executor e) -> doThingReturnsMono() .map(CacheResult::success) .onErrorResume(ex -> Mono.just(CacheResult.error(ex))) .subscribeOn(Schedulers.fromExecutor(e)) .toFuture() ); // 存入失败结果 cache.put("fail", Mono.error(new RuntimeException()) .map(CacheResult::success) .onErrorResume(ex -> Mono.just(CacheResult.error(ex))) .toFuture()); // 存入成功结果 cache.put("success", Mono.just("success") .map(CacheResult::success) .onErrorResume(ex -> Mono.just(CacheResult.error(ex))) .toFuture());
- 读取缓存时处理结果:根据封装类的类型分支处理成功或失败场景:
cache.get("fail").thenAccept(result -> { if (result instanceof SuccessResult<String> success) { System.out.println("成功值:" + success.value()); } else if (result instanceof ErrorResult<String> error) { System.err.println("错误信息:" + error.throwable().getMessage()); } });
替代方案(无需自定义类)
如果不想自定义封装类,也可以用Pair<Optional<T>, Optional<Throwable>>这类通用容器,但自定义密封接口的可读性和类型安全性更优。
内容的提问来源于stack exchange,提问作者A Jar of Clay
相关产品推荐
相关产品推荐

