问题描述
我使用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]
解决方法
我创建了一个特定的 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()
,
有一个更干净的方法。如果 spring-web
jar 在类路径上,Spring Boot 将创建一个启用 Kotlin 支持的 ObjectMapper
。如果不存在,只需将 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())
}
}
就是这样。