SpringBoot + Ehcache之自定义Key生成策略

今天在项目启用了ehcache缓存,但是某些方法使用缓存后不能正确的返回数据,抛出了类型转换异常,找了一番资料后发现是缓存的key生成策略问题,在此将此问题及解决办法记录一下。

spring cache缓存的key默认是通过KeyGenerator生成的,其默认生成策略如下:

  • 如果方法没有参数,则使用0作为key。
  • 如果只有一个参数的话则使用该参数作为key。
  • 如果参数多于一个的话则使用所有参数的hashCode作为key。

可以看出默认的key生成策略中并没有涉及方法名称和类,这就意味着如果我们有两个参数列表相同的方法,我们用相同的参数分别调用两个方法,当调用第二个方法的时候,spring cache将会返回缓存中的第一个方法的缓存值,因为他们的key是一样的。下面我们看一段代码:

@CacheConfig(cacheNames = "default")
public class SampleService {

  @Cacheable
  public Model1 getModel1(Integer id) {
    return // ...
  }

  @Cacheable
  public Model2 getModel2(Integer id) {
    return // ...
  }

}

场景:当我们先调用了getModel1(1),ehcache就会将方法的返回结果以"1"为key放入缓存中,当我们再调用getModel2(1)时,ehcache就会从缓存中找key为"1"的数据(即 Model1 )并试图将它转换为Model2 ,这就出现了异常: Model1 can not be cast to Model2.....

所以我们需要自定义key策略来解决这个问题,将类名和方法名和参数列表一起来生成key,下面是自定义的Key生成代码:

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * <b><code>CustomKeyGenerator</code></b>
 * <p>
 * Description: custom key generator of spring cache.
 * <p>
 * <b>Creation Time:</b> 2018/9/6 16:46
 *
 * @date 2018/9/6
 * @since JDK 1.7
 */
@Configuration
public class CustomKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        return new CustomKey(target.getClass(), method.getName(), params);
    }

    /**
     * Like {@link org.springframework.cache.interceptor.SimpleKey} but considers the method.
     */
    static final class CustomKey {

        private final Class<?> clazz;
        private final String methodName;
        private final Object[] params;
        private final int hashCode;

        /**
         * Initialize a key.
         *
         * @param clazz the receiver class
         * @param methodName the method name
         * @param params the method parameters
         */
        CustomKey(Class<?> clazz, String methodName, Object[] params) {
            this.clazz = clazz;
            this.methodName = methodName;
            this.params = params;
            int code = Arrays.deepHashCode(params);
            code = 31 * code + clazz.hashCode();
            code = 31 * code + methodName.hashCode();
            this.hashCode = code;
        }

        @Override
        public int hashCode() {
            return this.hashCode;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof CustomKey)) {
                return false;
            }
            CustomKey other = (CustomKey) obj;
            if (this.hashCode != other.hashCode) {
                return false;
            }

            return this.clazz.equals(other.clazz)
                    && this.methodName.equals(other.methodName)
                    && Arrays.deepEquals(this.params, other.params);
        }

    }

}

启用自定义的Key生成策略

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;

/**
 * <b><code>CustomCachingConfig</code></b>
 * <p>
 * Description: Custom Caching Config.
 * <p>
 * <b>Creation Time:</b> 2018/9/6 17:14
 *
 * @date 2018/9/6
 * @since JDK 1.7
 */
@Configuration
public class CustomCachingConfig extends CachingConfigurerSupport {

    @Override
    public KeyGenerator keyGenerator() {
        return new CustomKeyGenerator();
    }

}

代码摘自:A Better Spring Cache KeyGenerator
参考资料:https://blog.csdn.net/u013378306/article/details/52168628

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,477评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,955评论 18 399
  • 缓存是最直接有效提升系统性能的手段之一。个人认为用好用对缓存是优秀程序员的必备基本素质。 本文结合实际开发经验,从...
    Java小生阅读 4,280评论 1 3
  • 今年,梁鹤誉种了一棵小葫芦,还没有成熟的时候,她就说等葫芦熟了就卖葫芦。这几天,她都在出售自己的葫芦。小手里拿着一...
    向日葵3阅读 1,553评论 0 1
  • 上一节说完了配置文件的加载顺序,那么本节就来看一下struts.xml文件中各配置项的含义和说明。该文件是Stru...
    d03bd8f89ce7阅读 4,048评论 0 50