首先看看生成器接口的源码
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.
*
* 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生成策略,如果不同的包存在相同的参数列表和传递了相同的参数值,则在一定条件下,会导致访问到错误的缓存。所以我们重写生成器,来避免这个问题
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 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 包
commons-codec
commons-codec
// 在 CacheConfig 中定义
@Bean
KeyGenerator keyGenerator(){
return new RedisCacheKeyGenerator();
}
// 该值是 keyGenerator 方法的方法名称,如果Bean 指定了名称,则使用指定的名称
public static final String DEFAULT_KEY_GENERATOR = "keyGenerator";
// 定义缓存区,缓存区可以在配置时指定不同的过期时间,作为防止缓存雪崩的一个保护措施
public static final String COMMON = "COMMON";
@Cacheable(value = CacheConfig.COMMON,keyGenerator = CacheConfig.DEFAULT_KEY_GENERATOR)
COMMON::b525f46bc5dac06113cb9a3a9c094231db3a20dcd89bf36edebfd14ae9ee1500