【问题标题】:NotSerializableException with redisson-spring-boot-starter带有 redisson-spring-boot-starter 的 NotSerializableException
【发布时间】:2020-09-22 14:41:08
【问题描述】:

我正在使用 redisson-spring-boot-starter 3.13.2 和 Kotlin 进行缓存,但出现以下异常:

java.lang.IllegalArgumentException: java.io.NotSerializableException: com.service.message.State
    at org.redisson.RedissonObject.encodeMapValue(RedissonObject.java:338)
    at org.redisson.RedissonMapCache.fastPutOperationAsync(RedissonMapCache.java:843)
    at org.redisson.RedissonMapCache.fastPutAsync(RedissonMapCache.java:746)
    at org.redisson.RedissonMapCache.fastPut(RedissonMapCache.java:720)
    at org.redisson.spring.cache.RedissonCache.put(RedissonCache.java:107)
    at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
    at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:820)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:429)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at ...
Caused by: java.io.NotSerializableException: com.service.message.State
    at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:272)
    at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58)
    at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:111)
    at org.redisson.codec.MarshallingCodec$4.encode(MarshallingCodec.java:176)
    at org.redisson.RedissonObject.encodeMapValue(RedissonObject.java:336)
    ... 115 common frames omitted
Caused by: org.jboss.marshalling.TraceInformation: null

这是我的配置类的样子:

@Configuration
@EnableCaching
class CacheConfig {
    @Bean
    fun cacheManager(client: RedissonClient): CacheManager {
        val config = org.redisson.spring.cache.CacheConfig()
        config.ttl = 10 * 60 * 1000
        return RedissonSpringCacheManager(client, mapOf("State" to config))
    }
}

我要缓存的类型:

data class State(
    val phoneCode: String,
    val phoneNumber: String
)

这就是我试图缓存它的方式:

@CachePut(value = ["State"], key = "#id")
    fun initializeState(phoneAreaCode: String, phoneNumber: String, id: String): State {
        ...
        return State(
            phoneCode = "...",
            phoneNumber = "...",
            ...
        )
    }

编辑:我设法通过将 JsonJacksonCodec() 添加到 RedissonSpringCacheManager(client, mapOf("State" to config), JsonJacksonCodec()) 来绕过此错误,但现在我在反序列化时遇到了问题:

unable to decode data. channel: [id: 0x81736df6, L:/...], reply: ReplayingDecoderByteBuf(ridx=1704, widx=1704), command: (EVAL), params: [local value = redis.call('hget', KEYS[1], ARGV[2]); if value == false then return nil; end; local t,..., 5, ...
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'
 at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1695]

【问题讨论】:

  • 你试过用@Serializable注解标记你的State类吗?
  • 不是注释,但我尝试通过实现 Serializable 并且它似乎可以工作,但是一旦我在对象中添加另一种类型,它就会再次开始抱怨。另一种对象类型是我无法控制的,因为它是由 swagger 生成的。我还记得在 Lettuce 等其他实现中,您不需要实际实现可序列化接口。
  • 所有需要放入redis的对象都应该实现可序列化。如果您尝试序列化的对象是第 3 方类,那么您应该将其标记为瞬态。但是如果你仍然需要第 3 方类及其数据,你可以尝试在你的 redisconfig 中定义一个 JSONSerializer。不过我对 kotlin 不是很熟悉。
  • 你能告诉我如何在 redis config 中定义 JSONSerializer 或 deserializer 吗? Java 代码就可以了。
  • 好的,添加编解码器后,反序列化被破坏,如原始帖子中所述。

标签: spring-boot kotlin redis redisson


【解决方案1】:

我已经创建了一个特定的 Kotlin JsonJacksonCodec:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator
import org.redisson.codec.JsonJacksonCodec

class JsonJacksonKotlinCodec(objectMapper: ObjectMapper) : JsonJacksonCodec(objectMapper) {

    override fun initTypeInclusion(mapObjectMapper: ObjectMapper) {
        mapObjectMapper.activateDefaultTyping(BasicPolymorphicTypeValidator.builder()
            .allowIfBaseType(Any::class.java)
            .build(), ObjectMapper.DefaultTyping.EVERYTHING)
    }
}

此编解码器覆盖所有类型包含逻辑并包括所有内容的类型。数据类、密封类和一切正常。

使用它:

val config = Config()
val codec = JsonJacksonKotlinCodec(ObjectMapper())
codec.objectMapper.registerKotlinModule()
config.codec = codec
config.useSingleServer().address = redisConfig.host
redissonClient = Redisson.create(config).reactive()

【讨论】:

    【解决方案2】:

    有一种更清洁的方法。如果 spring-web jar 在 classpath 上,Spring Boot 将创建一个 ObjectMapper 并启用 Kotlin 支持。如果不存在,只需将 com.fasterxml.jackson.module:jackson-module-kotlin jar 添加到类路径,然后执行以下操作:

    创建可感知 Kotlin 的 ObjectMapper

    @Bean
    fun objectMapper(): ObjectMapper = jacksonObjectMapper()
    

    实现RedissonAutoConfigurationCustomizer回调接口:

    @Bean
    fun redisConfigCustomizer(): RedissonAutoConfigurationCustomizer {
        return RedissonAutoConfigurationCustomizer {
            it.codec = JsonJacksonKotlinCodec(objectMapper())
        }
    }
    

    就是这样。

    【讨论】:

      猜你喜欢
      • 2021-04-02
      • 2014-04-07
      • 1970-01-01
      • 2016-01-29
      • 1970-01-01
      • 2019-12-26
      • 2015-07-07
      • 2023-04-04
      • 1970-01-01
      相关资源
      最近更新 更多