Kann ich eine TTL für @Cacheable festlegen?

99

Ich probiere die @CacheableAnnotation-Unterstützung für Spring 3.1 aus und frage mich, ob es eine Möglichkeit gibt, die zwischengespeicherten Daten nach einiger Zeit durch Festlegen einer TTL zu löschen. Nach dem, was ich sehen kann, muss ich es selbst klären, indem ich das verwende @CacheEvict, und indem @Scheduledich das zusammen mit verwende, kann ich selbst eine TTL-Implementierung vornehmen, aber es scheint ein bisschen viel für eine so einfache Aufgabe zu sein?

Piotr
quelle

Antworten:

39

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

Wie kann ich die Funktion TTL / TTI / Räumungsrichtlinie / XXX einstellen?

Direkt über Ihren Cache-Anbieter. Die Cache-Abstraktion ist ... nun, eine Abstraktion, keine Cache-Implementierung

Wenn Sie also EHCache verwenden, verwenden Sie die Konfiguration von EHCache, um die TTL zu konfigurieren.

Sie können auch den CacheBuilder von Guava verwenden , um einen Cache zu erstellen, und die ConcurrentMap-Ansicht dieses Caches an die setStore-Methode der ConcurrentMapCacheFactoryBean übergeben .

JB Nizet
quelle
57

Frühling 3.1 und Guave 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
quelle
21
Erweitern Sie für Spring 4.1 CachingConfigurerSupport und überschreiben Sie nur cacheManager ().
Johannes Flügel
39

Hier finden Sie ein vollständiges Beispiel für die Einrichtung des Guava-Cache im Frühjahr. Ich habe Guava über Ehcache verwendet, weil es etwas leichter ist und mir die Konfiguration direkter erschien.

Maven-Abhängigkeiten importieren

Fügen Sie diese Abhängigkeiten zu Ihrer Maven-POM-Datei hinzu und führen Sie Clean und Pakete aus. Diese Dateien sind die Guava dep- und Spring-Hilfsmethoden zur Verwendung im 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>

Konfigurieren Sie den Cache

Sie müssen eine CacheConfig-Datei erstellen, um den Cache mithilfe der Java-Konfiguration zu konfigurieren.

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

Kommentieren Sie die zwischengespeicherte Methode

Fügen Sie die Annotation @Cacheable hinzu und übergeben Sie den Cache-Namen.

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

Ein vollständigeres Beispiel mit kommentierten Screenshots finden Sie hier: Guava Cache in Spring

anataliocs
quelle
2
Hinweis: Der Guaven-Cache ist im Frühjahr 5 ( stackoverflow.com/questions/44175085/… )
Amin Ziaei
32

Ich benutze Life Hacking so

@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
quelle
Rufen Sie die reportCacheEvictMethode von überall auf ? Wie passiert das cacheEvict?
Jaikrat
Kapiert. Wir rufen diese Methode nicht von irgendwoher auf. Es wird nach festem Verzögerungszeitintervall aufgerufen. Danke für den Tipp.
Jaikrat
1
Das Löschen des gesamten Caches nach einem Zeitplan kann ein praktischer Hack sein, um die Dinge zum Laufen zu bringen, aber diese Methode kann nicht verwendet werden, um Elementen eine TTL zu geben. Selbst der Bedingungswert kann nur deklarieren, ob der gesamte Cache gelöscht werden soll. Dem liegt die Tatsache zugrunde, dass ConcurrentMapCache Objekte ohne Zeitstempel speichert, sodass eine TTL nicht unverändert ausgewertet werden kann.
JMB
ist Code Seat-of-the-Pants (diese Methode wurde notiert :)).
Atum
Netter und sauberer Ansatz
Lauksas
30

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

}

und

@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}
jo8937
quelle
Tolle! Dies war genau das, wonach ich gesucht habe
MerLito
6

Dies kann durch Erweitern von org.springframework.cache.interceptor.CacheInterceptor und Überschreiben der "doPut" -Methode - org.springframework.cache.interceptor.AbstractCacheInvoker erfolgen. Ihre Überschreibungslogik sollte die Put-Methode des Cache-Providers verwenden, mit der TTL für den Cache-Eintrag festgelegt werden kann (In meinem Fall verwende ich 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);



}

In Ihrer Cache-Konfiguration müssen Sie diese 2 Bean-Methoden hinzufügen und Ihre benutzerdefinierte Interceptor-Instanz erstellen.

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


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

Diese Lösung eignet sich, wenn Sie die TTL auf der Einstiegsebene und nicht global auf der Cache-Ebene festlegen möchten

lukass77
quelle
2

Seit Spring-Boot 1.3.3 können Sie die Ablaufzeit in CacheManager mithilfe von RedisCacheManager.setExpires oder RedisCacheManager.setDefaultExpiration in der CacheManagerCustomizer- Rückruf-Bean festlegen .

Andrew
quelle
0

Bei Verwendung von Redis kann TTL in einer Eigenschaftendatei wie folgt festgelegt werden:

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
quelle
-2

Wenn Sie mit Redis und Java 8 arbeiten, können Sie sich JetCache ansehen :

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

Huang Li
quelle
1
Frage ist für Frühling @Cacheable Annotation
Satyesht