今天在項(xiàng)目啟用了ehcache緩存,但是某些方法使用緩存后不能正確的返回?cái)?shù)據(jù)氢伟,拋出了類型轉(zhuǎn)換異常高职,找了一番資料后發(fā)現(xiàn)是緩存的key生成策略問題疼约,在此將此問題及解決辦法記錄一下。
spring cache緩存的key默認(rèn)是通過KeyGenerator生成的逃贝,其默認(rèn)生成策略如下:
- 如果方法沒有參數(shù)谣辞,則使用0作為key做修。
- 如果只有一個(gè)參數(shù)的話則使用該參數(shù)作為key硝岗。
- 如果參數(shù)多于一個(gè)的話則使用所有參數(shù)的hashCode作為key。
可以看出默認(rèn)的key生成策略中并沒有涉及方法名稱和類舔腾,這就意味著如果我們有兩個(gè)參數(shù)列表相同的方法沪摄,我們用相同的參數(shù)分別調(diào)用兩個(gè)方法躯嫉,當(dāng)調(diào)用第二個(gè)方法的時(shí)候,spring cache將會(huì)返回緩存中的第一個(gè)方法的緩存值杨拐,因?yàn)樗麄兊膋ey是一樣的祈餐。下面我們看一段代碼:
@CacheConfig(cacheNames = "default")
public class SampleService {
@Cacheable
public Model1 getModel1(Integer id) {
return // ...
}
@Cacheable
public Model2 getModel2(Integer id) {
return // ...
}
}
場景:當(dāng)我們先調(diào)用了getModel1(1),ehcache就會(huì)將方法的返回結(jié)果以"1"為key放入緩存中哄陶,當(dāng)我們?cè)僬{(diào)用getModel2(1)時(shí)帆阳,ehcache就會(huì)從緩存中找key為"1"的數(shù)據(jù)(即 Model1 )并試圖將它轉(zhuǎn)換為Model2 ,這就出現(xiàn)了異常: Model1 can not be cast to Model2.....
所以我們需要自定義key策略來解決這個(gè)問題屋吨,將類名和方法名和參數(shù)列表一起來生成key蜒谤,下面是自定義的Key生成代碼:
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* <b><code>CustomKeyGenerator</code></b>
* <p>
* Description: custom key generator of spring cache.
* <p>
* <b>Creation Time:</b> 2018/9/6 16:46
*
* @date 2018/9/6
* @since JDK 1.7
*/
@Configuration
public class CustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
return new CustomKey(target.getClass(), method.getName(), params);
}
/**
* Like {@link org.springframework.cache.interceptor.SimpleKey} but considers the method.
*/
static final class CustomKey {
private final Class<?> clazz;
private final String methodName;
private final Object[] params;
private final int hashCode;
/**
* Initialize a key.
*
* @param clazz the receiver class
* @param methodName the method name
* @param params the method parameters
*/
CustomKey(Class<?> clazz, String methodName, Object[] params) {
this.clazz = clazz;
this.methodName = methodName;
this.params = params;
int code = Arrays.deepHashCode(params);
code = 31 * code + clazz.hashCode();
code = 31 * code + methodName.hashCode();
this.hashCode = code;
}
@Override
public int hashCode() {
return this.hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CustomKey)) {
return false;
}
CustomKey other = (CustomKey) obj;
if (this.hashCode != other.hashCode) {
return false;
}
return this.clazz.equals(other.clazz)
&& this.methodName.equals(other.methodName)
&& Arrays.deepEquals(this.params, other.params);
}
}
}
啟用自定義的Key生成策略
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;
/**
* <b><code>CustomCachingConfig</code></b>
* <p>
* Description: Custom Caching Config.
* <p>
* <b>Creation Time:</b> 2018/9/6 17:14
*
* @date 2018/9/6
* @since JDK 1.7
*/
@Configuration
public class CustomCachingConfig extends CachingConfigurerSupport {
@Override
public KeyGenerator keyGenerator() {
return new CustomKeyGenerator();
}
}
代碼摘自:A Better Spring Cache KeyGenerator
參考資料:https://blog.csdn.net/u013378306/article/details/52168628