Spring Boot中使用@Cacheable将可序列化Kotlin数据类缓存至Redis时遇到反序列化异常求助
Spring Boot中使用@Cacheable将可序列化Kotlin数据类缓存至Redis时遇到反序列化异常求助
嗨,我之前刚踩过几乎一模一样的坑!咱们先搞清楚问题出在哪,再一步步解决它~
问题根源
你现在用的是Spring Data Redis默认的JdkSerializationRedisSerializer,这个序列化器基于Java原生序列化机制,要求被序列化的类实现java.io.Serializable接口。但你给TrainDetails加的是Kotlinx Serialization的@Serializable注解——这是Kotlin专属的序列化体系,和Java原生序列化完全不兼容,所以反序列化时自然会报错啦。
解决方案:替换Redis序列化器
有两种常用方案,你可以根据项目情况选择:
方案一:用Kotlinx Serialization实现自定义Redis序列化器
既然已经引入了Kotlinx Serialization的依赖和插件,咱们直接写一个适配Redis的序列化器:
首先创建序列化器类:
import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import org.springframework.data.redis.serializer.RedisSerializer import org.springframework.data.redis.serializer.SerializationException import java.nio.charset.StandardCharsets class KotlinxRedisSerializer<T>(private val serializer: KSerializer<T>) : RedisSerializer<T> { private val json = Json { ignoreUnknownKeys = true // 兼容字段变更 encodeDefaults = true // 序列化默认值 } override fun serialize(t: T?): ByteArray? { return t?.let { json.encodeToString(serializer, it).toByteArray(StandardCharsets.UTF_8) } } override fun deserialize(bytes: ByteArray?): T? { return bytes?.let { json.decodeFromString(serializer, String(it, StandardCharsets.UTF_8)) } } }
然后配置Redis缓存管理器,让它使用这个序列化器:
import org.springframework.cache.annotation.EnableCaching import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.redis.cache.RedisCacheConfiguration import org.springframework.data.redis.cache.RedisCacheManager import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.serializer.RedisSerializationContext import org.springframework.data.redis.serializer.StringRedisSerializer @Configuration @EnableCaching class RedisCacheConfig { @Bean fun redisCacheManager(connectionFactory: RedisConnectionFactory): RedisCacheManager { // 指定TrainDetails的序列化器 val trainDetailsSerializer = KotlinxRedisSerializer(TrainDetails.serializer()) val cacheConfig = RedisCacheConfiguration.defaultCacheConfig() // 键用字符串序列化 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) // 值用自定义的Kotlinx序列化器 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(trainDetailsSerializer)) return RedisCacheManager.builder(connectionFactory) .cacheDefaults(cacheConfig) .build() } }
方案二:用Jackson配合Kotlin模块(更省心)
如果不想写自定义序列化器,也可以用Spring默认支持的Jackson来处理Kotlin数据类:
首先确保依赖里加了Jackson的Kotlin模块(Gradle Kotlin DSL为例):
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
然后配置缓存管理器:
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.registerKotlinModule import org.springframework.cache.annotation.EnableCaching import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.redis.cache.RedisCacheConfiguration import org.springframework.data.redis.cache.RedisCacheManager import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer import org.springframework.data.redis.serializer.RedisSerializationContext import org.springframework.data.redis.serializer.StringRedisSerializer @Configuration @EnableCaching class RedisCacheConfig { @Bean fun objectMapper(): ObjectMapper { // 注册Kotlin模块,让Jackson能正确处理Kotlin数据类 return ObjectMapper().registerKotlinModule() } @Bean fun redisCacheManager(connectionFactory: RedisConnectionFactory, objectMapper: ObjectMapper): RedisCacheManager { val jacksonSerializer = Jackson2JsonRedisSerializer(TrainDetails::class.java).apply { setObjectMapper(objectMapper) } val cacheConfig = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSerializer)) return RedisCacheManager.builder(connectionFactory) .cacheDefaults(cacheConfig) .build() } }
最后别忘了清理旧缓存!
如果你之前已经用JDK序列化存过数据到Redis,一定要先清空对应的缓存键(比如Schedule相关的键),不然新的序列化器还是读不懂旧格式的数据,会继续报错哦~
内容来源于stack exchange
相关产品推荐
相关产品推荐

