Spring 5中如何缓存WebClient返回的Flux?代码尝试未生效
解决Spring 5中WebClient返回Flux的缓存问题
你遇到的问题主要有两个核心原因:@Cacheable无法作用于private方法,以及响应式流Flux的冷流特性导致直接缓存实例无法达到预期效果。下面一步步给你梳理修正方案:
1. 先修复@Cacheable的方法可见性问题
Spring的缓存注解(比如@Cacheable)是基于AOP代理实现的,而代理只能拦截public方法——private方法不会被Spring的代理逻辑增强,所以你写的@Cacheable("testCache")完全没生效。
首先把getData方法改成public:
@Cacheable("testCache") public Flux<String> getData(String body) { return WebClient.create().post() .uri("http://myurl") .body(BodyInserters.fromValue(body)) .retrieve() .bodyToFlux(String.class); }
2. 适配响应式流的缓存逻辑
就算改成public方法,直接缓存Flux实例也可能达不到预期——因为Flux是冷流,每次订阅它都会重新执行上游的WebClient请求。要真正缓存请求结果,有两种实用方案:
方案一:缓存Flux的元素集合(适合数据量不大的场景)
把Flux转换成List缓存,返回时再转成Flux,这样缓存的是实际数据,而非冷流实例:
@Cacheable("testCache") public Flux<String> getData(String body) { // 收集Flux为List并缓存,注意block会阻塞线程,适合非高并发场景 List<String> resultList = WebClient.create().post() .uri("http://myurl") .body(BodyInserters.fromValue(body)) .retrieve() .bodyToFlux(String.class) .collectList() .block(); return Flux.fromIterable(resultList); }
如果不想阻塞线程,可以用Mono来缓存:
@Cacheable("testCache") public Mono<List<String>> getDataAsMono(String body) { return WebClient.create().post() .uri("http://myurl") .body(BodyInserters.fromValue(body)) .retrieve() .bodyToFlux(String.class) .collectList(); } // 在Controller里调用 public Flux<String> handleRequest(@RequestBody String body) { return getDataAsMono(body).flatMapMany(Flux::fromIterable); }
方案二:使用响应式缓存管理器(推荐高并发场景)
Spring 5原生支持响应式缓存,你可以使用支持响应式的缓存实现(比如Caffeine Reactive Cache、Redis Reactive Cache),配合ReactiveCacheManager,这样能直接缓存Flux/Mono且不会阻塞线程:
首先添加Caffeine相关依赖(以Maven为例):
<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
然后配置ReactiveCacheManager:
@Configuration public class ReactiveCacheConfig { @Bean public ReactiveCacheManager reactiveCacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager("testCache"); cacheManager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000)); return new ReactiveCacheManagerAdapter(cacheManager); } }
之后修改你的方法,直接返回Flux即可,Spring会自动处理响应式缓存逻辑:
@Cacheable("testCache") public Flux<String> getData(String body) { return WebClient.create().post() .uri("http://myurl") .body(BodyInserters.fromValue(body)) .retrieve() .bodyToFlux(String.class); }
这样缓存的是Flux的订阅结果,后续订阅会直接从缓存获取,不会重复发起WebClient请求。
额外注意点
- 确保缓存key生成符合预期:默认key是方法参数,若需要更复杂的规则,可以用
@Cacheable(key = "#body")明确指定。 - 如果WebClient可能返回空结果,可添加
cacheNullValues = true到@Cacheable注解中,支持缓存空值(根据Spring版本调整参数)。
内容的提问来源于stack exchange,提问作者Esteban S
相关产品推荐
相关产品推荐

