欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Cache 中keyGenerator生成策略源码解析与自定义

程序员文章站 2022-10-03 20:33:01
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

相关标签: 小记 Spring-Cache