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<String, Duration> 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<String, Duration> cacheMap;
        static {
            cacheMap = ImmutableMap.<String, Duration>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<RedisCacheManager> {
        /** CacheManager缓存自定义初始化比较早,尽量不要@autowired 其他spring 组件 */
        @Override
        public void customize(RedisCacheManager cacheManager) {
            // 自定义缓存名对应的过期时间
            Map<String, Long> expires = ImmutableMap.<String, Long>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方案的话,首次设置缓存数据之后,每次重复请求相同方法读取缓存并不会刷新失效时间,这是Spring的默认行为(受一些缓存影响,一直以为每次读缓存也会刷新缓存失效时间)。

可以参见源码:

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));

		// Collect puts from any @Cacheable miss, if no cached item is found
		List<CachePutRequest> 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 if 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的实现方案。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring缓存注解@Cacheable @CacheEvit @CachePut使用介绍

    目录 I. 项目环境 1. 项目依赖 II. 缓存注解介绍 1. @Cacheable 2. @CachePut 3. @CacheEvict 4. @Caching 5. 异常时,缓存会怎样? 6. 测试用例 7. 小结 III. 不能错过的源码和相关知识点 0. 项目 Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用起来还是很丝滑的,本文将针对几个常用的注解进行简单的介绍说明,有需要的小伙伴可以尝试一下 本文主要知识点: @Cacheable: 缓存存在,则使用缓存:不存在

  • 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用

    注释介绍 @Cacheable @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @Cacheable 作用和配置方法 参数 解释 example value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value="mycache") @Cacheable(value={"cache1","cache2"} key 缓存的 key,可以为空,如果指定要按照

  • SpringBoot使用@Cacheable时设置部分缓存的过期时间方式

    目录 使用@Cacheable时设置部分缓存的过期时间 业务场景 添加Redis配置类RedisConfig.java @Cacheable自定义缓存过期时间 pom yml RedisConfig CustomRedisCacheManager 使用 使用@Cacheable时设置部分缓存的过期时间 业务场景 Spring Boot项目中有一些查询数据需要缓存到Redis中,其中有一些缓存是固定数据不会改变,那么就没必要设置过期时间.还有一些缓存需要每隔几分钟就更新一次,这时就需要设置过期时间

  • Spring @Cacheable注解中key的使用详解

    目录 Spring @Cacheable注解中key使用 下面是几个使用参数作为key的示例 condition属性指定发生的条件 @CachePut @CacheEvict allEntries属性 beforeInvocation属性 @Caching 使用自定义注解 @Cacheable 拼接key Spring @Cacheable注解中key使用 key属性是用来指定Spring缓存方法的返回结果时对应的key的.该属性支持SpringEL表达式.当我们没有指定该属性时,Spring将

  • spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用

    maven项目中在pom.xml中依赖2个jar包,其他的spring的jar包省略: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.springfra

  • springboot增加注解缓存@Cacheable的实现

    目录 springboot增加注解缓存@Cacheable 业务层使用 配置 @Cacheable注解的属性使用 cacheNames和value key keyGenerator keyGenerator condition unless(除非) sync springboot增加注解缓存@Cacheable 业务层使用 @Cacheable(value = "dictionary#1800", key = "#root.targetClass.simpleName +':

  • 详解SpringBoot2.0的@Cacheable(Redis)缓存失效时间解决方案

    问题   @Cacheable注解不支持配置过期时间,所有需要通过配置CacheManneg来配置默认的过期时间和针对每个类或者是方法进行缓存失效时间配置. 解决   可以采用如下的配置信息来解决的设置失效时间问题 配置信息 @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager( RedisCacheWriter.no

  • Spring @Cacheable redis异常不影响正常业务方案

    背景 项目中,使用@Cacheable进行数据缓存.发现:当redis宕机之后,@Cacheable注解的方法并未进行缓存冲突,而是直接抛出异常.而这样的异常会导致服务不可用. 原因分析 我们是通过@EnableCaching进行缓存启用的,因此可以先看@EnableCaching的相关注释 通过@EnableCaching的类注释可发现,spring cache的核心配置接口为:org.springframework.cache.annotation.CachingConfigurer /**

  • Spring @Cacheable指定失效时间实例

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

  • Java之Spring注解配置bean实例代码解析

    前面几篇均是使用xml配置bean,如果有上百个bean,这是不可想象的.故而,请使用注解配置bean !!! [1]注解类别 @Component : 基本注解, 标识了一个受 Spring(点击这里可以下载<Spring应用开发完全手册>) 管理的组件 @Repository : 标识持久层组件 @Service : 标识服务层(业务层)组件 @Controller : 标识表现层组件 Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件. 对于扫描到的组

  • Spring Boot整合RabbitMQ实例(Topic模式)

    1.Topic交换器介绍 Topic Exchange 转发消息主要是根据通配符. 在这种交换机下,队列和交换机的绑定会定义一种路由模式,那么,通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息. 在这种交换机模式下: 路由键必须是一串字符,用句号(.) 隔开,比如说 agreements.us,或者 agreements.eu.stockholm 等. 路由模式必须包含一个 星号(*),主要用于匹配路由键指定位置的一个单词,比如说,一个路由模式是这样子:agreements..b.*

  • Spring的事务机制实例代码

    本文研究的主要是Spring的事务机制的相关内容,具体如下. JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持.而局部事务则与底层采用的持久化技术有关,如果底层直接使用JDBC,需要用Connection对象来操事务.如果采用Hibernate持久化技术,则需要使用session对象来操作事务. 通常的,使用JTA事务,JDBC事务及Hibern

  • spring学习之@SessionAttributes实例解析

    本文研究的主要是spring学习之@SessionAttributes的相关内容,具体如下. 一.@ModelAttribute 在默认情况下,ModelMap 中的属性作用域是 request 级别是,也就是说,当本次请求结束后,ModelMap中的属性将销毁.如果希望在多个请求中共享 ModelMap 中的属性,必须将其属性转存到 session 中,这样ModelMap 的属性才可以被跨请求访问. spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session

  • Spring实战之调用实例工厂方法创建Bean操作示例

    本文实例讲述了Spring实战之调用实例工厂方法创建Bean操作.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" x

  • Spring Cloud Feign组件实例解析

    这篇文章主要介绍了Spring Cloud Feign组件实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 采用Spring Cloud微服务框架后,经常会涉及到服务间调用,服务间调用采用了Feign组件. 由于之前有使用dubbo经验.dubbo的负载均衡策略(轮训.最小连接数.随机轮训.加权轮训),dubbo失败策略(快速失败.失败重试等等), 所以Feign负载均衡策略的是什么? 失败后是否会重试,重试策略又是什么?带这个疑问,查了

  • Spring+SpringMVC+Hibernate整合实例讲解

    使用Maven构建项目,用pom.xml引入相应jar,配置以下文件 创建spring.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns

  • Spring @Cacheable读取配置常量方式

    目录 Spring @Cacheable读取配置常量 属性①:value 属性②:key Spring缓存管理(Cacheable) 基于注解的支持 @Cacheable @CachePut @CacheEvict Spring @Cacheable读取配置常量 属性①:value String REDIS_DATABASE="database"; cacheable的name.默认会在后面加上双冒号,手动调用加上:: 属性②:key String REDIS_KEY_PREFIX=&

随机推荐