如何在Spring Redis Cache中结合Kotlinx Serialization实现多数据类的序列化与反序列化?
如何在Spring Redis Cache中结合Kotlinx Serialization实现多数据类的序列化与反序列化?
你完全不用给每个数据类都单独写一套RedisCacheConfiguration,针对你有10个数据类的场景,咱们可以用批量配置缓存映射的方式来简化代码,既保持灵活性又避免重复劳动。
方案一:基于缓存名-序列化器映射的批量配置
这个思路很直接:先维护一个「缓存名对应数据类序列化器」的映射表,然后批量生成每个缓存的专属配置,最后交给RedisCacheManager统一管理。
第一步:维护缓存名与序列化器的映射
先把你所有需要缓存的数据类对应的缓存名和序列化器整理到一个Map里:
// 可以放在配置类里作为一个属性 private val cacheSerializerMap = mapOf( "trainStatus" to TrainStatus.serializer(), "trainSchedule" to TrainSchedule.serializer(), "userDetails" to UserDetails.serializer(), // 剩下的7个数据类依次添加进来即可 )
第二步:改造CacheManager配置
基于你的现有代码,扩展成批量配置的形式:
@Bean fun cacheManager(redisConnectionFactory: RedisConnectionFactory): CacheManager { // 先定义通用的基础缓存配置(键序列化、过期时间等) val defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofDays(1)) .serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()) ) // 批量生成每个缓存名对应的专属配置(仅覆盖值序列化器) val cacheSpecificConfigs = cacheSerializerMap.mapValues { (_, serializer) -> defaultCacheConfig.serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( KotlinSerializationRedisSerializer(serializer) ) ) } return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(defaultCacheConfig) // 未在映射表中的缓存使用默认配置 .withInitialCacheConfigurations(cacheSpecificConfigs) // 加载批量配置的缓存 .build() }
第三步:正常使用@Cacheable
这样你就可以像最开始期望的那样,直接通过缓存名指定对应的缓存操作,Redis会自动匹配到正确的序列化器:
@Cacheable("trainStatus") suspend fun getTrainStatus(...): TrainStatus @Cacheable("userDetails") fun getUser(...): UserDetails
为什么这个方案适合你?
- 维护成本低:新增数据类时,只需要在
cacheSerializerMap里加一行映射即可,不用修改CacheManager的核心逻辑 - 保留原有序列化器:你写的
KotlinSerializationRedisSerializer完全可以继续用,不用做任何修改 - 灵活性高:如果某个缓存需要特殊配置(比如不同的过期时间),可以单独在映射里覆盖对应的配置
进阶思路:通用自动推断序列化器(可选)
如果你不想维护这个映射表,也可以尝试改造序列化器,让它能通过反射自动获取数据类的序列化器。不过这里有个前提:Redis存储的缓存值需要附带类型信息,否则反序列化时无法推断目标类型。
改造后的通用序列化器示例:
import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.serializer import org.springframework.data.redis.serializer.RedisSerializer import java.nio.charset.Charsets class GenericKotlinxRedisSerializer( private val json: Json = Json ) : RedisSerializer<Any> { @Suppress("UNCHECKED_CAST") override fun serialize(t: Any?): ByteArray? { return t?.let { // 通过反射获取当前对象的序列化器 val serializer = serializer(it::class.java) as KSerializer<Any> json.encodeToString(serializer, it).toByteArray(Charsets.UTF_8) } } @Suppress("UNCHECKED_CAST") override fun deserialize(bytes: ByteArray?): Any? { // 注意:这里默认无法直接反序列化,因为缺少类型信息 // 实际使用时需要结合Spring的缓存上下文获取目标类型,或者在序列化时存入类型信息 throw UnsupportedOperationException("请结合具体类型调用带类型参数的反序列化方法") } // 供Spring内部调用的带类型参数的反序列化方法 @Suppress("UNCHECKED_CAST") fun <T> deserialize(bytes: ByteArray?, targetType: Class<T>): T? { return bytes?.let { val serializer = serializer(targetType) as KSerializer<T> json.decodeFromString(serializer, String(it, Charsets.UTF_8)) } } }
不过这个进阶方案需要额外处理Spring缓存的类型传递逻辑,相对复杂一些。如果你的数据类数量固定在10个左右,前面的批量映射方案会更简单直接。
内容来源于stack exchange
相关产品推荐
相关产品推荐

