您的位置:首页 > 编程语言 > Java开发

Spring cache 自定义Key生成策略

2016-09-01 10:51 295 查看
摘要: Spring cache

最近使用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;

//使用Transient,不序列化这个字段
@Transient
private static final Set<Class<?>> BIZ_CLASS_TYPE;

//初始化
static{
//使用类扫描工具类, 扫描项目包中BaseModel.class的所有子类,把他们放到一个Set中
//换种思想,假设项目包比较规范,没有父类的情况可以扫描包
//比如ClassScaner.scan(new String []{"com.xxx.**.model","com.xxx.**.vo"});
BIZ_CLASS_TYPE = ClassScaner.scan("com.mypackage", BaseModel.class);
}

/** 调用目标对象全类名 */
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 !=null && BIZ_CLASS_TYPE.contains(ele.getClass())){
this.params[i] = ReflectUtil.beanToMap(ele);
}else{
this.params[i] = ele;
}
}
}
this.hashCode = generatorHashCode();
}

@Transient
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
@Transient
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;
}


最后在配置文件中注册使用自定义的key生成策略

<!-- Key生成策略配置 -->
<bean id="defaultKeyGenerator" class="xxx.cache.DefaultKeyGenerator"/>

<!-- 开启spring缓存注解 -->
<cache:annotation-driven cache-manager="cacheManager" key-generator="defaultKeyGenerator"/>

ClassScaner的代码 http://git.oschina.net/lis1314/codes/vxbyrealzs1uhi842g5no89#

ReflectUtil的代码 http://git.oschina.net/lis1314/codes/zilgs1caq7u89k3omjpw522
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息