Spring Boot控制器返回MappingJacksonValue时缓存序列化异常如何解决?
问题原因
你遇到的报错是因为Redis默认使用JDK序列化器来序列化缓存对象,该序列化器要求被缓存的类必须实现Serializable接口,而MappingJacksonValue作为Spring MVC提供的JSON动态序列化包装类,本身没有实现该接口,因此无法被默认序列化器处理。
可行方案
方案1:不修改返回类型,更换Redis缓存序列化器
你可以自定义Redis缓存配置,使用Jackson2JsonRedisSerializer替换默认的JDK序列化器,直接对MappingJacksonValue做JSON序列化:
@Configuration @EnableCaching class RedisCacheConfig { @Bean fun cacheConfiguration(objectMapper: ObjectMapper): RedisCacheConfiguration { val valueSerializer = Jackson2JsonRedisSerializer(objectMapper, MappingJacksonValue::class.java) return RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer)) .entryTtl(Duration.ofHours(1)) // 按需调整缓存过期时间 } }
该方案改造成本低,但存在明显缺陷:MappingJacksonValue属于视图层的包装对象,缓存该对象会导致业务逻辑和视图序列化逻辑耦合,如果后续调整JSON过滤规则,旧的缓存数据会直接不符合新的序列化要求,需要全量清理缓存才能生效。
方案2:拆分逻辑,修改缓存粒度(更推荐)
MappingJacksonValue本质是对业务数据做序列化规则包装的工具类,本身不需要被缓存。你可以将业务数据查询和JSON包装逻辑拆分,仅缓存可序列化的业务实体对象,每次请求时用缓存的业务对象动态生成MappingJacksonValue即可,改造后的代码参考:
@GetMapping("/{id}") fun getAccessRoleDetails( @PathVariable("id") @NotBlank(message = "Please provide a access role id") id: UUID ): MappingJacksonValue { // 从缓存取业务数据,缓存未命中时才查库 val accessRole = getCachedAccessRole(id) // 每次动态生成MappingJacksonValue,应用最新的过滤规则 val mappingJacksonValue = MappingJacksonValue(accessRole) mappingJacksonValue.filters = SimpleFilterProvider() .addFilter( "filterOne", SimpleBeanPropertyFilter.filterOutAllExcept( "id", "code", "createdDate", "description", "lastUpdated", "title", "accessControlPolicies" ) ) .addFilter( "filterTwo", SimpleBeanPropertyFilter.filterOutAllExcept( "id", "code", "createdDate", "description", "lastUpdated", ) ) return mappingJacksonValue } // 单独抽取出查询方法,仅缓存业务实体 @Cacheable(key = "#id") fun getCachedAccessRole(id: UUID): AccessRole { println( """ called in port ${serverProperties.port} """.trimIndent() ) val accessRole = accessRoleRepo.findById(id.toString()) if (accessRole.isEmpty) { throw GenericException( errorMessage = "not found", code = HttpStatus.NOT_FOUND.value() ) } return accessRole.get() }
该方案逻辑拆分清晰,业务层和视图层完全解耦,后续调整JSON过滤规则不需要清理缓存,稳定性和可维护性更好。
内容的提问来源于stack exchange,提问作者Sagar Nayak
相关产品推荐
相关产品推荐

