#StackBounty: #spring #jackson #spring-data-redis #spring-cache Why does Spring @Cacheable not pass the annotated method's result t…

Bounty: 50

This is sample code with Kotlin.

@Configuration

@Bean("cacheManager1hour")
fun cacheManager1hour(@Qualifier("cacheConfig") cacheConfiguration: RedisCacheConfiguration, redisConnectionFactory: RedisConnectionFactory): CacheManager {
    cacheConfiguration.entryTtl(Duration.ofSeconds(60 * 60))
    return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(cacheConfiguration)
            .build()
}

@Bean("cacheConfig")
fun cacheConfig(objectMapper:ObjectMapper): RedisCacheConfiguration {
    return RedisCacheConfiguration.defaultCacheConfig()
            .computePrefixWith { cacheName -> "yaya:$cacheName:" }
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer()))

}

@RestController

@Cacheable(value = "book", key = "#root.methodName", cacheManager = "cacheManager1hour")
fun getBook(): Book {
    return Book()
}

class Book  {
    var asdasd:String? = "TEST"
    var expires_in = 123
}

The GenericJackson2JsonRedisSerializer cannot process the “kotlin class” and we need to add ‘@class as property’ to the Redis cache entry.

Anyway, why do we need the @class? The Spring context is aware of the result’s type, why doesn’t it get passed? We would have two benefits:

  1. less memory
  2. easy for the Serializer, i.e. objectMapper.readValue(str, T)

Annotated Spring code for illustration

// org.springframework.cache.interceptor.CacheAspectSupport
@Nullable
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
    for (Cache cache : context.getCaches()) {
        // --> maybe we can pass the context.method.returnType to doGet
        Cache.ValueWrapper wrapper = doGet(cache, key); 
        if (wrapper != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Cache entry for key '" + key + "' found in cache '" + 
        cache.getName() + "'");
            }
            return wrapper;
        }
    }
    return null;
}

// org.springframework.data.redis.cache.RedisCache
@Override
protected Object lookup(Object key) { 
    // -> there will get the deserialized type can pass to Jackson

    byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));

    if (value == null) {
        return null;
    }

    return deserializeCacheValue(value);
}


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.