最近研究Spring+Redis缓存时,发现Cacheable注解在方法体上标注了之后虽然能够产生缓存,但是在redis中的缓存TIL是-1,接口返回的数据一直应用该缓存,导致缓存数据无法更新,网络上查询发现大都是通过注解中配置方便每个方法自定义缓存有效时间的方法。代码配置如下。
/**
*重新配置RedisCacheManager
* @param r
*/
@Autowired
public void configRedisCacheManger(RedisCacheManager r){
r.setDefaultExpiration(100L);
Map expires = Maps.newHashMap();
expires.put("tGet",60L);
// 设置缓存的过期时间和自动刷新时间
r.setExpires(expires);
}
@Cacheable(cacheNames = tGet",key = "#xzqh",sync = true)
public Txxx get(String xzqh){
......
}
查看源码后发现在RedisCache中会对三种注解的方法分别执行不同的方法,而我使用的@CacheAble正好使用的get方法。源码贴上
public T get(final Object key, final Callable valueLoader) {
//此处方法是应该完成过期时间增加,但是好像没有
BinaryRedisCacheElement rce = new BinaryRedisCacheElement(new RedisCacheElement(new RedisCacheKey(key).usePrefix(
cacheMetadata.getKeyPrefix()).withKeySerializer(redisOperations.getKeySerializer()), valueLoader),
cacheValueAccessor);
ValueWrapper val = get(key);
if (val != null) {
return (T) val.get();
}
RedisWriteThroughCallback callback = new RedisWriteThroughCallback(rce, cacheMetadata);
try {
byte[] result = (byte[]) redisOperations.execute(callback);
return (T) (result == null ? null : cacheValueAccessor.deserializeIfNecessary(result));
} catch (RuntimeException e) {
throw CacheValueRetrievalExceptionFactory.INSTANCE.create(key, valueLoader, e);
}
}
同样的put方法。
@Override
public void put(final Object key, final Object value) {
//然而这边有的很明显expireAfter()方法
put(new RedisCacheElement(new RedisCacheKey(key).usePrefix(cacheMetadata.getKeyPrefix()).withKeySerializer(
redisOperations.getKeySerializer()), value).expireAfter(cacheMetadata.getDefaultExpiration()));
}
同样测试@CachePut注解标注在接口上,接口数据正常缓存,且带预设的TIL值。
在RedisCache类中是使用内部类RedisWriteThroughCallBack的diInRedis()方法入库的,其中设置过期时长的方法为:
protected void processKeyExpiration(RedisCacheElement element, RedisConnection connection) {
if (!element.isEternal()) {
connection.expire(element.getKeyBytes(), element.getTimeToLive());
}
}
很明显element中未定义TimeToLive值,因此缓存在redis库中的数据TIL都是-1,数据不会过期。
解决方法:原来的spring boot版本为1.4.4- spring-data-redis版本为1.7.7,更改redis版本为1.5.15,spring-data-redis版本自动切换为1.8.14。发现RedisCache中的get方法被修改为
public T get(final Object key, final Callable valueLoader) {
//添加了expireAfter选项
RedisCacheElement cacheElement = new RedisCacheElement(getRedisCacheKey(key),
new StoreTranslatingCallable(valueLoader)).expireAfter(cacheMetadata.getDefaultExpiration());
BinaryRedisCacheElement rce = new BinaryRedisCacheElement(cacheElement, cacheValueAccessor);
ValueWrapper val = get(key);
if (val != null) {
return (T) val.get();
}
RedisWriteThroughCallback callback = new RedisWriteThroughCallback(rce, cacheMetadata);
try {
byte[] result = (byte[]) redisOperations.execute(callback);
return (T) (result == null ? null : fromStoreValue(cacheValueAccessor.deserializeIfNecessary(result)));
} catch (RuntimeException e) {
throw CacheValueRetrievalExceptionFactory.INSTANCE.create(key, valueLoader, e);
}
}
测试后发现缓存能够正常过期
PS:在springboot1.5以后,redis依赖中间添加了data,改为
org.springframework.boot
spring-boot-starter-data-redis