You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:17:15