Spring @Cacheable指定失效时间实例

网友投稿 279 2022-09-07


Spring @Cacheable指定失效时间实例

目录Spring @Cacheable指定失效时间新版本配置老版本配置@Cacheable缓存失效时间策略默认实现及扩展背景Spring Cache Redis实现Spring Cache 失效时间自行刷新

Spring @Cacheable指定失效时间

新版本配置

@Configuration

@EnableCaching

public class RedisCacheConfig {

@Bean

public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {

return (builder) -> {

for (Map.Entry entry : RedisCacheName.getCacheMap().entrySet()) {

builder.withCacheConfiguration(entry.getKey(),

RedisCacheConfiguration.defaultCacheConfig().entryTtl(entry.getValue()));

}

};

}

public static class RedisCacheName {

public static final String CACHE_10MIN = "CACHE_10MIN";

@Getter

private static final Map cacheMap;

static {

cacheMap = ImmutableMap.builder().put(CACHE_10MIN, Duration.ofSeconds(10L)).build();

}

}

}

老版本配置

interface CacheNames{

String CACHE_15MINS = "sssss:cache:15m";

/** 30分钟缓存组 */

String CACHE_30MINS = "sssss:cache:30m";

/** 60分钟缓存组 */

String CACHE_60MINS = "sssss:cache:60m";

/** 180分钟缓存组 */

String CACHE_180MINS = "sssss:cache:180m";

}

@Component

public class RedisCacheCustomizer

implements CacheManagerCustomizer {

/** CacheManager缓存自定义初始化比较早,尽量不要@autowired 其他spring 组件 */

@Override

public void customize(RedisCacheManager cacheManager) {

// 自定义缓存名对应的过期时间

Map expires = ImmutableMap.builder()

.put(CacheNames.CACHE_15MINS, TimeUnit.MINUTES.toSeconds(15))

.put(CacheNames.CACHE_30MINS, TimeUnit.MINUTES.toSeconds(30))

.put(CacheNames.CACHE_60MINS, TimeUnit.MINUTES.toSeconds(60))

.put(CacheNames.CACHE_180MINS, TimeUnit.MINUTES.toSeconds(180)).build();

// spring cache是根据cache name查找缓存过期时长的,如果找不到,则使用默认值

cacheManager.setDefaultExpiration(TimeUnit.MINUTES.toSeconds(30));

cacheManager.setExpires(expires);

}

}

@Cacheable(key = "key", cacheNames = CacheNames.CACHE_15MINS)

public String demo2(String key) {

return "abc" + key;

}

@Cacheable缓存失效时间策略默认实现及扩展

之前对Spring缓存的理解是每次设置缓存之后,重复请求会刷新缓存时间,但是问题排查阅读源码发现,跟自己的理解大相径庭。所有的你以为都仅仅是你以为!!!!

背景

目前项目使用的spring缓存,主要是CacheManager、Cache以及@Cacheable注解,Spring现有的缓存注解无法单独设置每一个注解的失效时间,Spring官方给的解释:Spring Cache是一个抽象而不是一个缓存实现方案。

因此对于缓存失效时间(TTL)的策略依赖于底层缓存中间件,官方给举例:ConcurrentMap是不支持失效时间的,而Redis是支持失效时间的。

Spring Cache Redis实现

Spring缓存注解@Cacheable底层的CacheManager与Cache如果使用Redis方案的话,首次设置缓存数据之后,每次重复请求相同方法读取缓存并不会刷新失效时间,这是SmKtOKkNapring的默认行为(受一些缓存影响,一直以为每次读缓存也会刷新缓存失效时间)。

可以参见源码:

org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {

// Special handling of synchronized invocation

if (contexts.isSynchronized()) {

CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();

if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {

Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);

Cache cache = context.getCaches().iterator().next();

try {

return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));

}

catch (Cache.ValueRetrievalException ex) {

// The invoker wraps any Throwable in a ThrowableWrapper instance so we

// can just make sure that one bubbles up the stack.

throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();

}

}

else {

// No caching required, only call the underlying method

return invokeOperation(invoker);

}

}

// Process any early evictions

processCacheEvicts(contexts.get(CacheEvictOperation.class), true,

CacheOperationExpressionEvaluator.NO_RESULT);

// Check if we have a cached item matching the conditions

Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));mKtOKkNa

// Collect puts from any @Cacheable miss, if no cached item is found

List cachePutRequests = new LinkedList<>();

if (cacheHit == null) {

collectPutRequests(contexts.get(CacheableOperation.class),

CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);

}

Object cacheValue;

Object returnValue;

if (cacheHit != null && !hasCachePut(contexts)) {

// If there are no put requests, just use the cache hit

cacheValue = cacheHit.get();

returnValue = wrapCacheValue(method, cacheValue);

}

else {

// Invoke the method mKtOKkNaif we don't have a cache hit

returnValue = invokeOperation(invoker);

cacheValue = unwrapReturnValue(returnValue);

}

// Collect any explicit @CachePuts

collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

// Process any collected put requests, either from @CachePut or a @Cacheable miss

for (CachePutRequest cachePutRequest : cachePutRequests) {

cachePutRequest.apply(cacheValue);

}

// Process any late evictions

processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

return returnValue;

}

因此如果我们需要自行控制缓存失效策略,就可能需要一些开发工作,具体如下。

Spring Cache 失效时间自行刷新

1:基于Spring的Cache组件进行定制,对get方法进行重写,刷新过期时间。相对简单,不难;此处不贴代码了。

2:可以使用后台线程进行定时的缓存刷新,以达到刷新时间的作用。

3:使用spring data redis模块,该模块提供对了TTL更新策略的,可以参见:org.springframework.data.redis.core.PartialUpdate

注意:

Spring对于@Cacheable注解是由spring-context提供的,spring-context提供的缓存的抽象,是一套标准而不是实现。

而PartialUpdate是由于spring-data-redis提供的,spring-data-redis是一套spring关于redis的实现方案。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:#yyds干货盘点#List反向函数reverse- python基础学习系列(69)
下一篇:那些让人拍手叫绝的 Python 代码(令人拍手称快)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~