我可以为@Cacheable 设置一个 TTL 吗

我正在尝试对 Spring 3.1的 @Cacheable注释支持,想知道是否有办法通过设置 TTL 在一段时间后清除缓存的数据? 现在我可以看到,我需要清除它自己使用 @CacheEvict,并通过使用它与 @Scheduled一起,我可以使一个 TTL 实现自己,但似乎有点多为这样一个简单的任务?

158592 次浏览

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#cache-specific-config:

如何设置 TTL/TTI/Eviction policy/XXX 特性?

直接通过您的缓存提供程序。缓存抽象是..。 抽象而不是缓存实现

因此,如果使用 EHCache,请使用 EHCache 的配置来配置 TTL。

您还可以使用 Guava 的 CacheBuilder来构建缓存,并将该缓存的 ConcurrentMap 视图传递给 ConcurrentMapCacheFactoryBean 的 setStore 方法

Spring 3.1和番石榴1.13.1:

@EnableCaching
@Configuration
public class CacheConfiguration implements CachingConfigurer {


@Override
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {


@Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(100).build().asMap(), false);
}
};


return cacheManager;
}


@Override
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}


}

下面是在 Spring 中设置番石榴缓存的完整示例。我使用了番石榴而不是 Ehcache,因为它的重量更轻,而且配置对我来说更直接。

导入 Maven 依赖项

将这些依赖项添加到 mavenpom 文件中,然后运行 clean 和 package。这些文件是用于 CacheBuilder 的 Guava dep 和 Spring 助手方法。

    <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>

配置缓存

您需要创建一个 CacheConfig 文件来使用 Java 配置配置缓存。

@Configuration
@EnableCaching
public class CacheConfig {


public final static String CACHE_ONE = "cacheOne";
public final static String CACHE_TWO = "cacheTwo";


@Bean
public Cache cacheOne() {
return new GuavaCache(CACHE_ONE, CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES)
.build());
}


@Bean
public Cache cacheTwo() {
return new GuavaCache(CACHE_TWO, CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.build());
}
}

注释要缓存的方法

添加@Cacheable 注释并传递缓存名称。

@Service
public class CachedService extends WebServiceGatewaySupport implements CachedService {


@Inject
private RestTemplate restTemplate;




@Cacheable(CacheConfig.CACHE_ONE)
public String getCached() {


HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);


HttpEntity<String> reqEntity = new HttpEntity<>("url", headers);


ResponseEntity<String> response;


String url = "url";
response = restTemplate.exchange(
url,
HttpMethod.GET, reqEntity, String.class);


return response.getBody();
}
}

您可以在这里看到一个带注释的截图的更完整的示例: 春天的番石榴缓存

我用这样的生活黑客技术

@Configuration
@EnableCaching
@EnableScheduling
public class CachingConfig {
public static final String GAMES = "GAMES";
@Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(GAMES);


return cacheManager;
}


@CacheEvict(allEntries = true, value = {GAMES})
@Scheduled(fixedDelay = 10 * 60 * 1000 ,  initialDelay = 500)
public void reportCacheEvict() {
System.out.println("Flush Cache " + dateFormat.format(new Date()));
}

由于 Spring-boot 1.3.3,您可以通过在 CacheManagerCustomizer回调 bean 中使用 SetExires 的 RedisCacheManager.setExires到期来设置 CacheManager 中的过期时间。

Springboot 1.3.8

import java.util.concurrent.TimeUnit;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.cache.CacheBuilder;


@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {


@Override
@Bean
public CacheManager cacheManager() {
GuavaCacheManager cacheManager = new GuavaCacheManager();
return cacheManager;
}


@Bean
public CacheManager timeoutCacheManager() {
GuavaCacheManager cacheManager = new GuavaCacheManager();
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(5, TimeUnit.SECONDS);
cacheManager.setCacheBuilder(cacheBuilder);
return cacheManager;
}


}

还有

@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}

这可以通过扩展 org.springframework.cache.ceptor.CacheInterceptor 来完成,并覆盖“ doPut”方法-org.springframework.cache.ceptor.AbstractCacheInvoker 您的覆盖逻辑应该使用缓存提供程序 put 方法,该方法知道为缓存条目设置 TTL (在我的例子中,我使用 HazelcastCacheManager)

@Autowired
@Qualifier(value = "cacheManager")
private CacheManager hazelcastCacheManager;


@Override
protected void doPut(Cache cache, Object key, Object result) {
//super.doPut(cache, key, result);
HazelcastCacheManager hazelcastCacheManager = (HazelcastCacheManager) this.hazelcastCacheManager;
HazelcastInstance hazelcastInstance = hazelcastCacheManager.getHazelcastInstance();
IMap<Object, Object> map = hazelcastInstance.getMap("CacheName");
//set time to leave 18000 secondes
map.put(key, result, 18000, TimeUnit.SECONDS);






}

在您的缓存配置上,您需要添加这两个 bean 方法,创建您的自定义拦截器实例。

@Bean
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}




@Primary
@Bean
public CacheInterceptor cacheInterceptor() {
CacheInterceptor interceptor = new MyCustomCacheInterceptor();
interceptor.setCacheOperationSources(cacheOperationSource());
return interceptor;
}

当您希望在入口级别设置 TTL,而不是在缓存级别设置全局时,这种解决方案是很好的

如果你正在使用 redis 和 Java8,你可以看看 JetCache:

@ Cached (过期 = 10,timeUnit = TimeUnit.MINUTES) User getUserById (long userId) ;

使用 Redis 时,可以在属性文件中设置 TTL,如下所示:

spring.cache.redis.time-to-live=1d # 1 day

spring.cache.redis.time-to-live=5m # 5 minutes

spring.cache.redis.time-to-live=10s # 10 seconds

对我来说最简单的方法是使用咖啡因缓存,它可以直接在您的 application.yml文件中配置。

您可以通过 expireAfterWrite参数设置 TTL:

spring:
cache:
caffeine:
spec: expireAfterWrite=15m
cache-names: mycache

这会在15分钟后驱逐元素。

基于 Redis 的解决方案

  • 弹簧启动: 2.2.6
  • 缓存存储: Redis (spring-boot-starter-cachespring-boot-starter-data-redis)
  • Java 版本: 8

实施方法:

import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;


import java.time.Duration;


@Configuration
public class CacheConfig {


@Bean
public RedisCacheManagerBuilderCustomizer customizer() {
return builder -> builder
.withCacheConfiguration("cacheKey1",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(14)))
.withCacheConfiguration("cacheKey2",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)))
.withCacheConfiguration("cacheKey3",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1)));
}
}