如何通过 Jackson 使用 json 在 Redis 中轻松缓存 Kotlin 对象?

How can I easily cache Kotlin Objects in Redis using json via Jackson?

我有一个用 Kotlin 编写的 Spring 启动应用程序,我想在其中启用 Redis 中的缓存。我希望将对象存储为序列化 JSON 并且理想情况下不希望必须注册可能被缓存的每种类型。我有一些基本有效的配置,但有一个很大的警告。

@Bean
fun redisCacheConfiguration(): RedisCacheConfiguration {
    val objectMapper =
        ObjectMapper()
            .registerModule(KotlinModule())
            .registerModule(JavaTimeModule())
            .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)

    val serializer = GenericJackson2JsonRedisSerializer(objectMapper)

    return RedisCacheConfiguration
        .defaultCacheConfig()
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
}

我有点难以理解 DefaultTyping 的不同值,但 NON_FINAL 似乎是最广泛的。然而,由于 Kotlin 中的对象默认是最终的,这只适用于标记为 "open" 的对象。理想情况下,我想避免 "open" 对象只是为了它们可以被缓存。

我还有其他方法可以完成这项工作吗?

我遇到了同样的问题。您应该使用 "open" classes。但这对数据 classes 没有帮助,因为你不能使它们 "open".
有一个名为 "all-open" 的插件,您可以在其中定义注释。如果使用这些注解 classes 就变成了 "open",即使数据 classes.

spring-kotlin 插件在后台使用 "all-open" 插件,因此 spring 像 @Service、@Component 等注解使 class 对 AOP 开放,因为代理需要你继承自 classes.

如果你使用 spring-kotlin 插件,有一个很好的注释对你的问题有意义,它被用在 Spring 缓存中,它的名字是 @Cacheable。 如果您在 classes 上使用 @Cacheable,它们将打开并将其类型信息保存到 json(例如:{@class: "com.example.MyClass", ... }) 当您包含此代码时:

    val objectMapper =
    ObjectMapper()
        .registerModule(KotlinModule())
        .registerModule(JavaTimeModule())
        .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)

val serializer = GenericJackson2JsonRedisSerializer(objectMapper)

更多详情:https://kotlinlang.org/docs/reference/compiler-plugins.html

简而言之:除了向您想要的 classes 添加 @Cacheable 批注外,您无需执行任何操作,而且它在我看来也很合适。

issues已解决。因此我们可以从代码中删除 @Cacheble hack。您必须使用下一个实现

修改 ObjectMapper
val om = ObjectMapper()
            .registerModule(KotlinModule())
            .registerModule(JavaTimeModule())
            .activateDefaultTyping(BasicPolymorphicTypeValidator.builder()
                .allowIfBaseType(Any::class.java)
                .build(), ObjectMapper.DefaultTyping.EVERYTHING)

val serializer = GenericJackson2JsonRedisSerializer(om)

修复了 Maven Jackon 依赖性

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0.pr2</version>
</dependency>

你可以看这个:

https://github.com/endink/caching-kotlin

它同时支持 jackson 和 kryo

我遇到了一个问题,因为我的数据 类 正在扩展一些接口,所以泛型无法解决问题,我最终得到了这个解决方案,它是一个自定义序列化器和反序列化器,泛型只会节省时间将 getter 编译为变量并破坏反序列化

@Configuration
@EnableCaching
class CachingConfiguration() : CachingConfigurerSupport() {

    @Bean
    fun configureRedisAction(): ConfigureRedisAction? {
        return ConfigureRedisAction.NO_OP
    }

    @Autowired
    private lateinit var redisConnectionFactory: RedisConnectionFactory

    companion object {
        const val CACHE_KEY = "cache-key"
    }

    @Bean
    override fun cacheManager(): CacheManager? {
        return RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory)
                .withCacheConfiguration(CACHE_KEY, cacheConfig<User>(ofMinutes(5)))
                .build()
    }

    private inline fun <reified T> cacheConfig(ttl: Duration): RedisCacheConfiguration {
        return RedisCacheConfiguration
                .defaultCacheConfig()
                .serializeValuesWith(fromSerializer(object : RedisSerializer<Any> {
                    val mapper = ObjectMapper().registerModule(ParameterNamesModule())
                    override fun serialize(t: Any?): ByteArray? {
                        return mapper.writeValueAsBytes(t)
                    }
                    override fun deserialize(bytes: ByteArray?): Any? {
                        return try {
                            mapper.readValue(bytes!!, T::class.java) as Any
                        } catch (e: Exception) {
                            null
                        }
                    }
                })
                )
                .entryTtl(ttl)
    }
}