简单理解自定义 KeyGenerator
一般情况我们在 spring boot 中会使用 redis 作为缓存 但我们是需要自定义 cache key 的生成方式
1.为什么不使用 spring 默认的 生成策略?
先看看源码:
public class DefaultKeyGenerator implements KeyGenerator {
public static final int NO_PARAM_KEY = 0;
public static final int NULL_PARAM_KEY = 53;
public DefaultKeyGenerator() {
}
public Object generate(Object target, Method method, Object... params) {
if (params.length == 0) {
//没有参数直接返回 0
return 0;
} else {
if (params.length == 1) {
Object param = params[0];
if (param == null) {
//有一个参数 并且为 null 返回 53
return 53;
}
//参数不是数组 直接返回参数
if (!param.getClass().isArray()) {
return param;
}
}
//其它情况 返回 深度计算的 hashcode 简单理解可以认为就是一个 hashcode
return Arrays.deepHashCode(params);
}
}
}
DefaultKeyGenerator 最终返回的 缓存键值当参数列表的值相同时是一样的 这样就会造成获取到错误的缓存数据
spring 还有一个 生成策略 同样也是有这样的 问题
public class SimpleKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
return generateKey(params);
}
/**
* Generate a key based on the specified parameters.
*/
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
}
public class SimpleKey implements Serializable {
public static final SimpleKey EMPTY = new SimpleKey();
private final Object[] params;
private final int hashCode;
/**
* Create a new {@link SimpleKey} instance.
* @param elements the elements of the key
*/
public SimpleKey(Object... elements) {
Assert.notNull(elements, "Elements must not be null");
this.params = new Object[elements.length];
System.arraycopy(elements, 0, this.params, 0, elements.length);
this.hashCode = Arrays.deepHashCode(this.params);
}
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof SimpleKey
&& Arrays.deepEquals(this.params, ((SimpleKey) obj).params)));
}
@Override
public final int hashCode() {
return this.hashCode;
}
@Override
public String toString() {
return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
}
}
最后 cache key 使用的是这个
return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
这也会在一定的情况下出现获取到错误的缓存数据
所以我们需要自己自定义 cache key 的生成方式
我一般是这样用的 类名+方法名+参数列表的类型+参数值 再做 哈希散列 作为key
@Bean
public KeyGenerator myKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
sb.append("&");
for (Object obj : params) {
if (obj != null){
sb.append(obj.getClass().getName());
sb.append("&");
sb.append(JSON.toJSONString(obj));
sb.append("&");
}
}
log.info("redis cache key str: "+sb.toString());
log.info("redis cache key sha256Hex: "+DigestUtils.sha256Hex(sb.toString()));
return DigestUtils.sha256Hex(sb.toString());
}
};
}
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。