spring-bootredisspring-dataspring-cacheredis-cache

ClassCastException in Spring Redis Cache


I'm developing a Spring Boot application with Spring Boot version 2.1.8.RELEASE. I need to build custom RedisCacheManager.

RedisCacheManager is as follows.

@EnableCaching
@Configuration
class CacheConfig {
    @Bean
    fun redisCacheManager(lettuceConnectionFactory: RedisConnectionFactory): RedisCacheManager? {
        val redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1))

        return RedisCacheManager.RedisCacheManagerBuilder
            .fromConnectionFactory(lettuceConnectionFactory)
            .cacheDefaults(redisCacheConfiguration)
            .build()
    }
}

In my service, I cache response with @Cacheble. See:

@Cacheable(cacheNames = ["cached_sample"])
    fun getAllSample(): List<SampleRecord> {
        return auditableRepository.findAll()
    }

Model I cached:

data class SampleRecord(
    @ApiModelProperty(readOnly = true)
    val id: Long? = null,
    @ApiModelProperty(readOnly = true)
    val active: Boolean? = null,
    @ApiModelProperty(readOnly = true)
    val createdDate: Instant? = null,
    val param: String
): Serializable

When I call function second time, I get following exception

Caused by: java.lang.ClassCastException: com.cryptocurrency.exchange.sample.model.SampleRecord cannot be cast to com.cryptocurrency.exchange.sample.model.SampleRecord

What whould be the reason of this exception?


Solution

  • This issue occurs if you Spring dev-tools in your dependency tree. There is a fairly easy solution, but it isn't documented well. You need to set the Redis cache config to reference the Context classloader when deserializing objects. For your code, it would look like:

    redisCacheConfiguration = RedisCacheConfiguration
    .defaultCacheConfig(Thread.currentThread().getContextClassLoader())
    .entryTtl(Duration.ofHours(1))
    

    The .defaultCacheConfig(Thread.currentThread().getContextClassLoader()) makes sure that Redis has a reference to the Context classloader when deserializing. Spring outlines this in the Known Issues sections here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-devtools-known-restart-limitations