Spring Cache 中keyGenerator生成策略源码解析与自定义
程序员文章站
2022-05-04 14:22:25
Spring Cache 中keyGenerator生成策略源码解析与自定义一:源码解析解释:一个缓存名对应一个被注解的方法,但是一个方法可能传入不同的参数,那么结果也就会不同,这应该如何区分呢?这就需要用到 key 。在 spring 中,key 的生成有两种方式:显式指定和使用 keyGenerator 自动生成。1、keyGeneratorkeyGenerator生成器其实是一个接口,下面先看下其源码:public interface KeyGenerator {/** * G...
Spring Cache 中keyGenerator生成策略源码解析与自定义
一:源码解析
解释:一个缓存名对应一个被注解的方法,但是一个方法可能传入不同的参数,那么结果也就会不同,这应该如何区分呢?这就需要用到 key 。在 spring 中,key 的生成有两种方式:显式指定和使用 keyGenerator 自动生成。
1、keyGenerator
keyGenerator生成器其实是一个接口,下面先看下其源码:
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);
}
2:DefaultKeyGenerator
当使用@Cacheable注解并且不显示指定key的时候就会调用默认实现类DefaultKeyGenerator
@Deprecated
public class DefaultKeyGenerator implements KeyGenerator {
public static final int NO_PARAM_KEY = 0;
public static final int NULL_PARAM_KEY = 53;
@Override
public Object generate(Object target, Method method, Object... params) {
//当没有参数时,返回 0(默认返回0)
if (params.length == 0) {
return NO_PARAM_KEY;
}
if (params.length == 1) {
Object param = params[0];
//当有一个参数并且参数为空,则返回53(标识任意值)
if (param == null) {
return NULL_PARAM_KEY;
}
//如果参数不为空同时参数类型不是数组的时候,则返回当前参数本身
if (!param.getClass().isArray()) {
return param;
}
}
//其它情况 返回深度计算的 hashcode
return Arrays.deepHashCode(params);
}
}
深度哈希Code?感觉很牛逼的样子,看看源码:
public static int deepHashCode(Object a[]) {
//如果参数为空,返回0
if (a == null)
return 0;
int result = 1;
//不为空,则遍历数组中所有元素,获取到所有元素的哈希值并且累计
for (Object element : a) {
int elementHash = 0;
if (element instanceof Object[])
elementHash = deepHashCode((Object[]) element);
else if (element instanceof byte[])
elementHash = hashCode((byte[]) element);
else if (element instanceof short[])
elementHash = hashCode((short[]) element);
else if (element instanceof int[])
elementHash = hashCode((int[]) element);
else if (element instanceof long[])
elementHash = hashCode((long[]) element);
else if (element instanceof char[])
elementHash = hashCode((char[]) element);
else if (element instanceof float[])
elementHash = hashCode((float[]) element);
else if (element instanceof double[])
elementHash = hashCode((double[]) element);
else if (element instanceof boolean[])
elementHash = hashCode((boolean[]) element);
else if (element != null)
elementHash = element.hashCode();
result = 31 * result + elementHash;
}
return result;
}
看到这里有没有小伙伴和我有一样的疑惑,result = 31 * result + elementHash中,31到底是干啥用的???通过查询资料可知:
原因:因为任何数n * 31就可以被JVM优化为 (n << 5) -n,移位和减法的操作效率要比乘法的操作效率高的多
补充下hashCode基础知识:
(1)hashCode是用来在散列存储结构中确定对象的存储地址,能快速的查找;
(2)两个对象相同,那么hashCode 一定相同,但是两个hashCode相同的对象不一样相同;
(3)如果重写对象的equals方法,那么也尽量重写对象的hashCode方法;
3、SimpleKeyGenerator
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) + "]";
}
}
二:自定义KeyGenerator
如在参数为map时,要求map里的所有key-value组合作为一个key来做缓存;
@Component
public class MyKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
Object param = params[0];
// 参数为map自定义key=类名+方法名+map的key-value值
if (param instanceof Map) {
StringBuilder builder = new StringBuilder();
String sp = ".";
builder.append(target.getClass().getSimpleName()).append(sp);
builder.append(method.getName()).append(sp);
Map<String, Object> map = (Map<String, Object>) param;
if (map.isEmpty()) {
return builder.toString();
}
for (String key : map.keySet()) {
builder.append(key).append("-").append(map.get(key)).append(sp);
}
return builder.toString();
}
return new SimpleKey(params);
}
}
参考博文:
SpringBoot+SpringCache+Redis整合,自定义KeyGenerator生成器,@Cacheable设置单个key的缓存时间
本文地址:https://blog.csdn.net/qq_37606901/article/details/109554770