1. 程式人生 > >Springboot 2.0.x Redis快取Key生成器,自定義生成器

Springboot 2.0.x Redis快取Key生成器,自定義生成器

文章目錄

Springboot 2.0.x Redis快取Key生成器,自定義生成器


1、預設的Key生成策略

首先看看生成器介面的原始碼


package org.springframework.cache.interceptor;

import java.lang.reflect.Method;

/**
 * Cache key generator. Used for creating a key based on the given method
 * (used as context) and its parameters.
 *
 * @author Costin Leau
 * @author Chris Beams
 * @author Phillip Webb
 * @since 3.1
 */
@FunctionalInterface
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);

}



然後看預設的Key 生成策略


package org.springframework.cache.interceptor;

import java.lang.reflect.Method;

/**
 * Simple key generator. Returns the parameter itself if a single non-null
 * value is given, otherwise returns a {@link SimpleKey} of the parameters.
 *
 * <p>No collisions will occur with the keys generated by this class.
 * The returned {@link SimpleKey} object can be safely used with a
 * {@link org.springframework.cache.concurrent.ConcurrentMapCache}, however,
 * might not be suitable for all {@link org.springframework.cache.Cache}
 * implementations.
 *
 * @author Phillip Webb
 * @author Juergen Hoeller
 * @since 4.0
 * @see SimpleKey
 * @see org.springframework.cache.annotation.CachingConfigurer
 */
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) {
			// 如果沒有引數,就構建一個new Object[] 作為引數
			return SimpleKey.EMPTY;
		}
		if (params.length == 1) {
			Object param = params[0];
			if (param != null && !param.getClass().isArray()) {
				return param;
			}
		}
		return new SimpleKey(params);
	}

}

然後是SimpleKey 的原始碼

	/**
	 * Create a new {@link SimpleKey} instance.
	 * @param elements the elements of the key
	 */
	public SimpleKey(Object... elements) {
		// 首先斷言,引數列表不是null,否則丟擲 IllegalArgumentException
		Assert.notNull(elements, "Elements must not be null");
		this.params = new Object[elements.length];
		// 組裝引數作為Key
		System.arraycopy(elements, 0, this.params, 0, elements.length);
		this.hashCode = Arrays.deepHashCode(this.params);
	}

一般情況下,預設的Key生成策略,如果不同的包存在相同的引數列表和傳遞了相同的引數值,則在一定條件下,會導致訪問到錯誤的快取。所以我們重寫生成器,來避免這個問題

2、重寫生成器
import com.alibaba.fastjson.JSON;
import com.zyfycs.college.core.ModelContainer;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;

/**
 * @author Created by 譚健 on 2018/11/21. 星期三. 14:53.
 * © All Rights Reserved.
 */

public class RedisCacheKeyGenerator implements KeyGenerator {
    @Override

    public Object generate(Object targetClass, Method method, Object... params) {
	// 這裡可用HashMap
        ModelContainer<String,Object> container = ModelContainer.newModelContainer();
        Class<?> targetClassClass = targetClass.getClass();
        // 類地址
        container.put("class",targetClassClass.toGenericString());
        // 方法名稱
        container.put("methodName",method.getName());
        // 包名稱
        container.put("package",targetClassClass.getPackage());
        // 引數列表
        for (int i = 0; i < params.length; i++) {
            container.put(String.valueOf(i),params[i]);
        }
        // 轉為JSON字串
        String jsonString = JSON.toJSONString(container);
        // 做SHA256 Hash計算,得到一個SHA256摘要作為Key
        return DigestUtils.sha256Hex(jsonString);
    }
}

DigestUtils 包

<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
</dependency>
3、註冊自定義生成器
	// 在 CacheConfig 中定義
    @Bean
    KeyGenerator keyGenerator(){
        return new RedisCacheKeyGenerator();
    }

	// 該值是 keyGenerator 方法的方法名稱,如果Bean 指定了名稱,則使用指定的名稱
	public static final String DEFAULT_KEY_GENERATOR = "keyGenerator";
	
	// 定義快取區,快取區可以在配置時指定不同的過期時間,作為防止快取雪崩的一個保護措施
	public static final String COMMON = "COMMON";
4、應用
@Cacheable(value = CacheConfig.COMMON,keyGenerator = CacheConfig.DEFAULT_KEY_GENERATOR)
5、在Redis 中Key的顯示
COMMON::b525f46bc5dac06113cb9a3a9c094231db3a20dcd89bf36edebfd14ae9ee1500