Spring Boot响应式控制器方法使用@Cacheable注解集成Redis缓存的异常问题及解决方案咨询
解决Spring Boot响应式方法Redis缓存序列化异常的方案
这个问题我之前在响应式项目里踩过坑,核心原因很明确:Spring Cache默认会直接缓存方法的返回值——也就是你代码里的Mono<Product>,但Redis的默认序列化器只支持实现Serializable的类型,Mono显然不满足这个要求,所以才抛出了那个异常。下面是两种推荐的解决方式,你可以根据自己的场景选择:
方案一:使用Spring官方的响应式缓存支持
从Spring Boot 2.2版本开始,框架对响应式类型(Mono/Flux)的缓存有了原生支持,只需要配置对应的响应式缓存管理器即可,不需要大幅修改现有代码:
配置ReactiveRedisCacheManager
创建一个自定义的缓存管理器Bean,替换默认的同步版本,同时指定支持JSON序列化的序列化器(避免要求实体类实现Serializable):@Configuration public class ReactiveRedisCacheConfig { @Bean public ReactiveRedisCacheManager reactiveRedisCacheManager(ReactiveRedisConnectionFactory connectionFactory) { RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return ReactiveRedisCacheManager.builder(connectionFactory) .cacheDefaults(cacheConfig) .build(); } }修正并保留原有的@Cacheable注解
你的原方法代码可以基本不变,但要修正@Cacheable的key表达式(因为方法参数名是sku,原来的#id会导致SpEL表达式找不到变量):@Cacheable(value = "products", key = "#sku") @GetMapping(value = "/product/{id}") public Mono<Product> getProduct(@PathVariable("id") String sku) { // 你的业务逻辑代码 }配置完成后,框架会自动识别返回的Mono类型,订阅它拿到实际的Product对象后再进行缓存,而不是缓存Mono本身。
方案二:手动处理响应式缓存逻辑
如果需要更灵活的缓存控制(比如自定义过期时间、缓存失效策略),可以直接使用ReactiveRedisTemplate手动实现缓存的读写逻辑:
@RestController public class ProductController { @Autowired private ReactiveRedisTemplate<String, Product> reactiveRedisTemplate; // 假设你有一个获取Product的业务服务 @Autowired private ProductService productService; @GetMapping(value = "/product/{id}") public Mono<Product> getProduct(@PathVariable("id") String sku) { String cacheKey = "products:" + sku; // 先从缓存读取 return reactiveRedisTemplate.opsForValue().get(cacheKey) // 如果缓存为空,执行原业务逻辑并写入缓存 .switchIfEmpty(Mono.defer(() -> productService.getProductById(sku) .doOnNext(product -> // 写入缓存,这里可以自定义过期时间,比如1小时 reactiveRedisTemplate.opsForValue().set(cacheKey, product, Duration.ofHours(1)).subscribe() ) )); } }
这种方式完全绕过了Spring Cache的自动处理,直接操作实际的Product对象,从根源上避免了缓存Mono的问题,同时也能更精细地控制缓存行为。
额外注意事项
- 不管用哪种方案,都要保证Product对象可以被正确序列化:推荐使用
GenericJackson2JsonRedisSerializer(方案一中的配置),它不需要实体类实现Serializable,而且序列化后的内容是可读的JSON格式,方便调试。 - 如果坚持用JDK序列化器,那你的Product类必须实现
Serializable接口,同时要注意关联对象的序列化问题。
内容的提问来源于stack exchange,提问作者kayelbb
相关产品推荐
相关产品推荐

