我在服务器端使用 Spring 缓存(使用 EHCache)来定义 @Cacheable 中的缓存键。问题是不同的客户端发送相同的字符串,这些字符串用作具有不同拼写的键,因为它们区分大小写。结果是我的缓存包含的对象比它们本来应该包含的要多。
例子: 假设我为某个方法定义了以下缓存:
@Cacheable(value = "myCache", key="{#myString}")
public SomeBusinessObject getFoo(String myString, int foo){
...
}
现在客户端 A 向 Controller 发送“abc”(全部小写)。 Controller 调用 getFoo 并将“abc”用作将对象放入缓存的键。 客户端 B 发送“abC”(大写 C),而不是返回键“abc”的缓存对象,而是创建键“abC”的新缓存对象。
如何避免按键区分大小写?
我知道我可以像这样将缓存键定义为小写:
@Cacheable(value = "myCache", key="{#myString.toLowerCase()}")
public SomeBusinessObject getFoo(String myString, int foo){
...
}
这当然有效。但我正在寻找更通用的解决方案。我有许多缓存和许多缓存键并执行一些@CacheEvict(s) 和@CachePut(s),如果我要使用“toLowerCase”方法,我将始终必须确保不要在任何地方忘记它。
最佳答案
如@gaston 所述,解决方案是替换默认的 KeyGenerator
。在您的 Configuration
中实现 org.springframework.cache.annotation.CachingConfigurer
或扩展 org.springframework.cache.annotation.CachingConfigurerSupport
。
@Configuration
@EnableCaching
public class AppConfig extends CachingConfigurerSupport {
@Override
public KeyGenerator keyGenerator() {
return new MyKeyGenerator();
}
@Bean
@Override
public CacheManager cacheManager() {
//replaced with prefered CacheManager...
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}
}
这是从 org.springframework.cache.interceptor.SimpleKeyGenerator
修改而来的实现。
import java.lang.reflect.Method;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKey;
public class MyKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null) {
if (param.getClass().isArray()) {
return new MySimpleKey((Object[])param);
} else {
if (param instanceof String) {
return ((String)param).toLowerCase();
}
return param;
}
}
}
return new MySimpleKey(params);
}
}
当 @Cacheable
方法有多个参数时,原始实现使用 SimpleKey
类生成 key 。
这是生成不区分大小写 key 的另一种实现方式。
import java.io.Serializable;
import java.util.Arrays;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@SuppressWarnings("serial")
public class MySimpleKey implements Serializable {
private final Object[] params;
private final int hashCode;
/**
* Create a new {@link SimpleKey} instance.
* @param elements the elements of the key
*/
public MySimpleKey(Object... elements) {
Assert.notNull(elements, "Elements must not be null");
Object[] lceles = new Object[elements.length];
this.params = lceles;
System.arraycopy(elements, 0, this.params, 0, elements.length);
for (int i = 0; i < elements.length; i++) {
Object o = elements[i];
if (o instanceof String) {
lceles[i] = ((String)o).toLowerCase();
} else {
lceles[i] = o;
}
}
this.hashCode = Arrays.deepHashCode(lceles);
}
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof MySimpleKey
&& Arrays.deepEquals(this.params, ((MySimpleKey) obj).params)));
}
@Override
public final int hashCode() {
return this.hashCode;
}
@Override
public String toString() {
return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
}
}
关于java - 如何避免 Spring 缓存中区分大小写的键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41484900/