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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 07:57:01