Spring WebFlux集成Caffeine缓存:@Cacheable未缓存Mono发射值的问题及相关疑问
咱们先理清楚你遇到的核心问题:用@Cacheable注解标注返回Mono的方法时,缓存的居然是Mono对象本身,而不是它最终发射的实际字符串值——这就导致每次从缓存取出Mono后,订阅它还是会触发原始的infoProvider.getInfo(id)调用,完全没起到缓存数据的作用。我来给你拆解问题原因、解决办法,还有你关心的为什么直接加.cache()行不通。
一、问题根源:用错了缓存启用注解
你大概率是在配置类上用了普通的@EnableCaching注解,但这个注解是给同步方法设计的!它根本不理解Reactive类型(Mono/Flux)的特性,只会把返回的Mono实例当作普通对象缓存起来,完全不会去订阅它获取实际值。
二、正确的解决步骤
要让@Cacheable正确缓存Mono发射的实际值,只需要做一个关键调整,再配合你的现有配置即可:
替换缓存启用注解
把配置类上的@EnableCaching换成@EnableReactiveCaching——这是Spring专门为Reactive场景提供的缓存启用注解,它会自动适配Mono/Flux类型,帮你订阅Reactive流、提取实际值并缓存,而不是缓存流本身。举个简单的配置类例子:
@Configuration @EnableReactiveCaching public class CacheConfig { }保留现有配置与业务代码
你当前的application.yml配置是完全没问题的,业务方法上的@Cacheable(value = "stuff")也不需要改。调整后,当你第一次调用getInfo(1)时,Spring会自动订阅infoProvider.getInfo(1)得到实际字符串,把这个字符串缓存到Caffeine的stuff缓存中;后续再调用同一个id的请求时,会直接从缓存取出字符串包装成Mono返回,不会再触发原始的数据源调用。
三、为什么直接加.cache()行不通?
你之前想的return infoProvider.getInfo(id).cache(),本质上是Mono自身的实例级局部缓存,和Spring Cache的全局缓存完全不是一回事,有两个核心问题:
- 缓存范围仅限当前Mono实例:每次调用
getInfo(id)都会创建一个全新的Mono对象,它的.cache()缓存只对这个实例有效。下次再调用getInfo(id),又会生成新的Mono,还是会去调用infoProvider.getInfo(id),根本没用到全局缓存。 - 没有全局缓存策略支持:Mono的
.cache()只能简单缓存值,无法实现你配置的maximumSize=1000、expireAfterAccess=3600s这类全局缓存规则,完全满足不了生产环境的缓存管控需求。
所以这种方式不仅解决不了你的问题,还会让你误以为已经缓存了数据,实际上每次请求还是会走原始数据源。
内容来源于stack exchange

