Czy mogę ustawić TTL dla @Cacheable

106

Staram się o @Cacheablewsparcie dla adnotacji wiosny 3.1 i zastanawiasz się, czy jest jakiś sposób, aby dane w pamięci podręcznej usunąć po pewnym czasie przez ustawienie TTL? W tej chwili z tego, co widzę, muszę sam to wyczyścić za pomocą @CacheEvict, a używając tego razem z @Scheduledmogę samodzielnie wykonać implementację TTL, ale wydaje się, że to trochę za dużo jak na tak proste zadanie?

Piotr
źródło

Odpowiedzi:

39

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

Jak mogę ustawić funkcję TTL / TTI / Eviction / XXX?

Bezpośrednio przez dostawcę pamięci podręcznej. Abstrakcja pamięci podręcznej jest ... cóż, abstrakcją, a nie implementacją pamięci podręcznej

Tak więc, jeśli używasz EHCache, użyj konfiguracji EHCache, aby skonfigurować TTL.

Możesz również użyć CacheBuilder Guavy do zbudowania pamięci podręcznej i przekazać widok ConcurrentMap tej pamięci podręcznej do metody setStore ConcurrentMapCacheFactoryBean .

JB Nizet
źródło
58

Wiosna 3.1 i Guawa 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();
    }

}
Magnus Heino
źródło
21
W przypadku Spring 4.1 rozszerz CachingConfigurerSupport i nadpisuj tylko cacheManager ().
Johannes Flügel
40

Oto pełny przykład konfiguracji Guava Cache na wiosnę. Użyłem guawy zamiast Ehcache, ponieważ jest nieco lżejszy, a konfiguracja wydawała mi się prostsza.

Importuj zależności Maven

Dodaj te zależności do pliku pom maven i uruchom czyste i pakiety. Te pliki są metodami pomocniczymi Guava dep i Spring do użycia w 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>

Skonfiguruj pamięć podręczną

Musisz utworzyć plik CacheConfig, aby skonfigurować pamięć podręczną przy użyciu konfiguracji 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());
   }
}

Opisz metodę, która ma być buforowana

Dodaj adnotację @Cacheable i podaj nazwę pamięci podręcznej.

@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();
    }
}

Możesz zobaczyć bardziej kompletny przykład tutaj z opisami zrzutów ekranu: Guava Cache in Spring

anataliocs
źródło
2
Uwaga: pamięć podręczna guawy jest teraz przestarzała na wiosnę 5 ( stackoverflow.com/questions/44175085/… )
Amin
35

Używam hakowania życia w ten sposób

@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()));
}
Atum
źródło
Czy wywołujesz reportCacheEvictmetodę z dowolnego miejsca. Jak dzieje się cacheEvict?
Jaikrat
Zdobyć. Nie wywołujemy tej metody z dowolnego miejsca. Jest wywoływana po interwale czasu fixedDelay. Dzięki za podpowiedź.
Jaikrat
1
Wyczyszczenie całej pamięci podręcznej zgodnie z harmonogramem może być poręcznym hakiem, aby wszystko działało, ale ta metoda nie może być używana do nadawania elementom TTL. Nawet wartość warunku może tylko zadeklarować, czy usunąć całą pamięć podręczną. U podstaw tego leży fakt, że ConcurrentMapCache przechowuje obiekty bez żadnego znacznika czasu, więc nie ma sposobu, aby ocenić TTL tak, jak jest.
jmb
to kod siedziska spodni (ta metoda została zapisana :)).
Atum
Ładne i czyste podejście
lauksas
31

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

}

i

@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}
jo8937
źródło
Niesamowity! Właśnie tego szukałem
MerLito
7

można to zrobić rozszerzając org.springframework.cache.interceptor.CacheInterceptor i zastępując metodę „doPut” - org.springframework.cache.interceptor.AbstractCacheInvoker Twoja logika override powinna używać metody put dostawcy pamięci podręcznej, która wie, jak ustawić TTL dla wpisu pamięci podręcznej (w moim przypadku używam 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);



}

w konfiguracji pamięci podręcznej musisz dodać te dwie metody fasoli, tworząc niestandardową instancję przechwytującą.

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


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

To rozwiązanie jest dobre, gdy chcesz ustawić TTL na poziomie podstawowym, a nie globalnie na poziomie pamięci podręcznej

lukass77
źródło
1

Podczas korzystania z Redis TTL można ustawić w pliku właściwości w następujący sposób:

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

Hamid Mohayeji
źródło
-2

Jeśli pracujesz z redis i Java 8, możesz rzucić okiem na JetCache :

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

Huang Li
źródło
1
pytanie jest na wiosnę @Cacheable adnotation
satyesht