Generating unique cache key with Spring KeyGenerator not working

blacktide picture blacktide · Dec 19, 2014 · Viewed 19.8k times · Source

I'm having the issue when my cache keys are colliding in Spring using the @Cacheable annotation. For instance, with the following two methods:

@Cacheable("doOneThing")
public void doOneThing(String name) {
  // do something with name
}

@Cacheable("doAnotherThing")
public void doAnotherThing(String name) {
  // do some other thing with name
}

Here is my cache configuration, in which I've added a keyGenerator and a cacheManager bean:

@Configuration
@EnableCaching
public class CacheConfig {

  @Bean
  public JedisConnectionFactory redisConnectionFactory() {
    return new JedisConnectionFactory();
  }

  @Bean
  public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
    RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
    redisTemplate.setConnectionFactory(cf);
    return redisTemplate;
  }

  @Bean
  public CacheManager cacheManager(RedisTemplate redisTemplate) {
    return new RedisCacheManager(redisTemplate);
  }

  @Bean
  public KeyGenerator keyGenerator() {
    return new KeyGenerator() {
      @Override
      public Object generate(Object o, Method method, Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(o.getClass().getName());
        sb.append(method.getName());
        for (Object param : params) {
          sb.append(param.toString());
        }
        return sb.toString();
      }
    };
  }
}

For some reason, the cache key always gets set to the name parameter in the method, not the result of the keyGenerator.generate(..) method, causing both methods to return the same cache result.

I know that I can specify the key manually on each @Cacheable annotation, but that seems a little extensive for every method I'd like to cache.

Edit

I've noticed that setting the keyGenerator option inside of the @Cacheable annotation to the name of my bean resolves the issue, like so:

@Cacheable(value = "doOneThing", keyGenerator = "keyGenerator")
public void doOneThing(String name) {
  // do something with name
}

And setting the keyGenerator option in the @CacheConfig annotation on the class also resolves the issue. It seems that this shouldn't be necessary though. Am I missing something?

Answer

Stephane Nicoll picture Stephane Nicoll · Dec 20, 2014

Your configuration should implement CachingConfigurer (usually you would extend from CachingConfigurerSupport) to customize how caching works.