IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Spring cache 自定义Key生成策略

    summer发表于 2016-07-08 13:00:06
    love 0

    最近使用Spring cache,发现使用默认生成的key策略只使用了方法入参作为key,很不灵活,用到真实的项目中也不太靠谱,于是自己实现它的key生成策略。

    参考官方文档:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

    spring 默认的key生成实现是

    org.springframework.cache.interceptor.SimpleKeyGenerator

    感兴趣的同学自己看下,具体我就不描述了。

    自定义生成策略需要实现: 接口对应方法参数,需要调用的目标对象实例,目标方法,目标方法入参

    public interface KeyGenerator {

    /**
    * Generate a key for the given method and its parameters.
    * @param target the target instance
    * @param method the method being called
    * @param params the method parameters (with any var-args expanded)
    * @return a generated key
    */
    Object generate(Object target, Method method, Object... params);

    }

    下面展示我实现的方式,仅供参考

    /**
    * 实现spring cache的默认缓存实现策略
    * @author lisuo
    *
    */
    public class DefaultKeyGenerator implements KeyGenerator {

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

    }

    这里必须要知道的是,spring cache在判断是否命中数据时,是拿着生成的(key对象)调用它的是equals进行匹配,如equals相同则为命中,返回缓存数据,其次key对象必须是实现Serializable接口的。

    下面展示我的具体Key实现:结合我的业务(说白了就是拿着现有的参数,目标对象,目标方法,目标入参,生成一个key对象,如果下一个对象参数一直,让他们的equals相同,就可以命中数据了..)

    /**
    * Spring cache key 生成策略
    * 类名+方法名+参数信息
    * 如果key的hashCode与equals一致,认为是同一个Key
    * 如果传入对象是BaseModel,那么便利它所有的一级属性,如果所有一级属性的hashCode一致,
    * 则认为Key相同
    * @author lisuo
    */
    public class DefaultKey implements Serializable{

    private static final long serialVersionUID = 1930236297081366076L;

    /** 调用目标对象全类名 */
    private String targetClassName;
    /** 调用目标方法名称 */
    private String methodName;
    /** 调用目标参数 */
    private Object[] params;

    private final int hashCode;

    public DefaultKey(Object target, Method method, Object[] elements) {
    this.targetClassName = target.getClass().getName();
    this.methodName = generatorMethodName(method);
    if(ArrayUtils.isNotEmpty(elements)){
    this.params = new Object[elements.length];
    for(int i =0;i<elements.length;i++){
    Object ele = elements[i];
    if(ele instanceof BaseModel){
    this.params[i] = ReflectUtil.beanToMap(ele);
    }else{
    this.params[i] = ele;
    }
    }
    }
    this.hashCode = generatorHashCode();
    }

    private String generatorMethodName(Method method){
    StringBuilder builder = new StringBuilder(method.getName());
    Class<?>[] types = method.getParameterTypes();
    if(ArrayUtils.isNotEmpty(types)){
    builder.append("(");
    for(Class<?> type:types){
    String name = type.getName();
    builder.append(name+",");
    }
    builder.append(")");
    }
    return builder.toString();
    }

    //生成hashCode
    private int generatorHashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + hashCode;
    result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
    result = prime * result + Arrays.hashCode(params);
    result = prime * result + ((targetClassName == null) ? 0 : targetClassName.hashCode());
    return result;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    DefaultKey other = (DefaultKey) obj;
    if (hashCode != other.hashCode)
    return false;
    if (methodName == null) {
    if (other.methodName != null)
    return false;
    } else if (!methodName.equals(other.methodName))
    return false;
    if (!Arrays.equals(params, other.params))
    return false;
    if (targetClassName == null) {
    if (other.targetClassName != null)
    return false;
    } else if (!targetClassName.equals(other.targetClassName))
    return false;
    return true;
    }

    @Override
    public final int hashCode() {
    return hashCode;
    }

    }

    其实代码比较简单,重要点在于:ReflectUtil.beanToMap(ele);那一行,把javaBean转换成Map。

    查看具体的Map底层对equals的实现,它是对它包含的所以数据进行equals比较的:

    参考java.util.AbstractMap<K, V>

    public boolean equals(Object o) {
    if (o == this)
    return true;

    if (!(o instanceof Map))
    return false;
    Map<?, ?> m = (Map<?, ?>) o;
    if (m.size() != size())
    return false;

    try {
    Iterator<Entry<K, V>> i = entrySet().iterator();
    while (i.hasNext()) {
    Entry<K, V> e = i.next();
    K key = e.getKey();
    V value = e.getValue();
    if (value == null) {
    if (!(m.get(key) == null && m.containsKey(key)))
    return false;
    } else {
    if (!value.equals(m.get(key)))
    return false;
    }
    }
    } catch (ClassCastException unused) {
    return false;
    } catch (NullPointerException unused) {
    return false;
    }

    return true;
    }

     

    具体的ReflectUtil.beanToMap(ele);如何实现参考下面工程中ReflectUtil类。

    http://git.oschina.net/lis1314/easy-excel



沪ICP备19023445号-2号
友情链接