SpringCacheRedis 程序重启后历史缓存反序列化失败

问题描述

实体类:

@Entity
@ApiModel(value="xxxxx")
class KmlRoute : BaseModel() {
    @ManyToMany(cascade = [CascadeType.REMOVE])
    @JoinTable(
        name = "kml_route_path",
        joinColumns = [JoinColumn(name = "kml_route_id")],
        inverseJoinColumns = [JoinColumn(name = "path_id")]
    )
    var paths: MutableList<Path>? = null
}

方法:

    @Cacheable(value = ["kml"], key="#id")
    fun findKmlRouteById(id: Int): KmlRoute {
        logger.info("Find kmlRoute by id: {}", id)
        return kmlRouteRepository.findById(id).orElse(null)
    }

第一次访问findKmlRouteById方法,缓存信息到redis中,之后将程序重启,再次访问findKmlRouteById方法,KmlRoute类paths反序列化失败,报null异常(不重启就不会有问题)

问题原因

跟踪源码可得到@Cacheable 主要处理方法为 CacheAspectSupport类的execute方法
在这里插入图片描述
进入到findCachedItem方法中一路跟踪,可获得获取redis值的地方为RedisCache中的deserializeCacheValue方法
在这里插入图片描述
此处发现问题出现在序列化这里,决定替换此序列化方法,redisCache自带的方法有六种
在这里插入图片描述
其中GenericJackson2JsonRedisSerializer可以解决List反序列化问题,但与hibernate的懒加载存在冲突,所以此处采用fastjson替换序列化方式

解决办法

修改redis序列化方式,使用FastJson替换redis默认的JdkSerializationRedisSerializer

创建FastJsonRedisSerializer

class FastJsonRedisSerializer<T>(private val clazz: Class<T>) : RedisSerializer<T?> {
    @Throws(SerializationException::class)
    override fun serialize(t: T?): ByteArray? {
        return if (t == null) {
            ByteArray(0)
        } else JSON.toJSONString(t, SerializerFeature.WriteClassName).toByteArray(DEFAULT_CHARSET)
    }

    @Throws(SerializationException::class)
    override fun deserialize(bytes: ByteArray?): T? {
        if (bytes == null || bytes.size <= 0) {
            return null
        }
        val str = String(bytes, DEFAULT_CHARSET)
        return JSON.parseObject(str, clazz) as T
    }

    companion object {
        val DEFAULT_CHARSET = Charset.forName("UTF-8")
    }
}

修改cacheManager

@Bean
 fun cacheManager(redisConnectionFactory: RedisConnectionFactory): CacheManager {
        val config = RedisCacheConfiguration.defaultCacheConfig()
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(FastJsonRedisSerializer(Any::class.java)))
        return RedisCacheManager
            .builder(redisConnectionFactory).cacheDefaults(config)
            .build()
    }
posted @ 2021-03-08 17:08  夏天hipo  阅读(347)  评论(0编辑  收藏  举报