I am trying out the @Cacheable
annotation support for Spring 3.1 and wondering if there is any way to make the cached data clear out after a time by setting a TTL?
Right now from what I can see I need to clear it out myself by using the @CacheEvict
, and by using that together with @Scheduled
I can make a TTL implementation myself but it seems a bit much for such a simple task?

- 3,246
- 4
- 24
- 42

- 4,813
- 7
- 35
- 46
11 Answers
Spring 3.1 and Guava 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();
}
}

- 1,183
- 9
- 12
-
34For Spring 4.1 extend CachingConfigurerSupport and only overwrite cacheManager(). – Johannes Flügel Jun 30 '16 at 16:12
-
3@JohannesFlügel Why do not post any example code? – Sep 14 '21 at 05:23
-
@Henry It nearly looks the same like in the example above. Simply replace `CacheConfiguration implements CachingConfigurer` by `CacheConfiguration extends CachingConfigurerSupport`. The latter one already implements `CachingConfigurer` by default methods. So you don't need to implement them if you don't want to. – Johannes Flügel Sep 14 '21 at 05:54
-
Thanks for reply. Sorry, but I am beginner and after trying many approach, I am afraid I could not find a proper solution. So, would you mind posting this as answer on my question --> **https://stackoverflow.com/questions/69169170/how-to-set-expiration-for-cacheable-in-spring-boot** – Sep 14 '21 at 05:58
I use life hacking like this
@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()));
}

- 1,211
- 1
- 12
- 14
-
Are you calling `reportCacheEvict` method from anywhere. How the cacheEvict happening?? – Jaikrat Dec 15 '16 at 10:29
-
Get it. We are not calling this method from anywhere. Its called after fixedDelay time interval. Thanks for the hint. – Jaikrat Dec 15 '16 at 11:29
-
7Clearing the entire cache on a schedule can be a handy hack to get things working, but this method can't be used to give items a TTL. Even the *condition* value can only declare whether to delete the entire cache. Underlying this is the fact that ConcurrentMapCache stores objects without any timestamp, so there's no way to evaluate a TTL as-is. – jmb May 10 '17 at 17:20
-
-
-
Small addition: you can use fixedDelayString, initialDelayString (from spring properties ${your.ttl.parameter.name}) and specify time unit in java.time.Duration format, for example: pt10m will set delay to 10minutes pt30s will set delay to 30 seconds and so on. – Kirill Mikhailov Feb 28 '23 at 12:59
How can I set the TTL/TTI/Eviction policy/XXX feature?
Directly through your cache provider. The cache abstraction is... well, an abstraction not a cache implementation
So, if you use EHCache, use EHCache's configuration to configure the TTL.
You could also use Guava's CacheBuilder to build a cache, and pass this cache's ConcurrentMap view to the setStore method of the ConcurrentMapCacheFactoryBean.

- 678,734
- 91
- 1,224
- 1,255
Here is a full example of setting up Guava Cache in Spring. I used Guava over Ehcache because it's a bit lighter weight and the config seemed more straight forward to me.
Import Maven Dependencies
Add these dependencies to your maven pom file and run clean and packages. These files are the Guava dep and Spring helper methods for use in the CacheBuilder.
<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>
Configure the Cache
You need to create a CacheConfig file to configure the cache using Java config.
@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());
}
}
Annotate the method to be cached
Add the @Cacheable annotation and pass in the cache name.
@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();
}
}
You can see a more complete example here with annotated screenshots: Guava Cache in Spring

- 176,835
- 32
- 241
- 292

- 10,427
- 6
- 56
- 72
-
6Note: Guava cache is now deprecated in Spring 5 (https://stackoverflow.com/questions/44175085/why-spring-deprecate-guava-cache-in-official-document) – Amin Oct 02 '18 at 17:40
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;
}
}
and
@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}

- 570
- 5
- 11
This can be done by extending org.springframework.cache.interceptor.CacheInterceptor
, and override doPut
method - org.springframework.cache.interceptor.AbstractCacheInvoker
your override logic should use the cache provider put method that knows to set TTL for cache entry (in my case I use 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);
}
on your cache configuration you need to add those 2 bean methods , creating your custom interceptor instance .
@Bean
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
@Primary
@Bean
public CacheInterceptor cacheInterceptor() {
CacheInterceptor interceptor = new MyCustomCacheInterceptor();
interceptor.setCacheOperationSources(cacheOperationSource());
return interceptor;
}
This solution is good when you want to set the TTL on the entry level, and not globally on cache level
-
I am trying to follow your approach and created my own cache interceptor by extending inbuilt one. But when actually "Cacheable" annotation gets executed, it keeps calling in-built interceptor and never calls my own interceptor. Do I need to change any setting anywhere apart from putting @Primary tag on my method? – user1108687 Aug 20 '21 at 15:08
-
try to add this to your application.properties - so at run time it will use your config, spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration ,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration - it means that your config is responsible to create also the CacheManager @Bean – lukass77 Aug 24 '21 at 18:47
When using Redis, TTL can be set in properties file like this:
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

- 3,977
- 3
- 43
- 55
-
-
This is a bit limiting since it will set only one TTL. In real world app you will want to have different TTL for different cache entries. – Alexis Jul 12 '22 at 11:55
The easiest way for me was using the Caffeine cache which is configurable directly in your application.yml
file.
You can setup the TTL by the expireAfterWrite
parameter. Example configuration would look as follows:
spring:
cache:
caffeine:
spec: expireAfterWrite=15m
cache-names: mycache
This will evict the elements after 15 minutes.

- 1,105
- 12
- 16
-
3Is this evict all cached data in `mychace` or just items that wrote 15m ago? – sansari Jul 20 '22 at 10:33
Since Spring-boot 1.3.3, you may set expire time in CacheManager by using RedisCacheManager.setExpires or RedisCacheManager.setDefaultExpiration in CacheManagerCustomizer call-back bean.
Redis based solution
- Spring boot: 2.2.6
- Cache store: Redis (
spring-boot-starter-cache
along withspring-boot-starter-data-redis
) - Java version: 8
Implementation:
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)));
}
}

- 794
- 1
- 8
- 17
-
It is also compatible with properties from application.yml, so you can combine them – HereAndBeyond May 12 '22 at 13:18
-
1This forces us to use a cacheName that we don't really want. I prefer to have different cacheManager defined so that I have control on the cache name. – Alexis Jul 12 '22 at 11:56