¿Puedo configurar un TTL para @Cacheable?

102

Estoy probando el @Cacheablesoporte de anotaciones para Spring 3.1 y me pregunto si hay alguna forma de borrar los datos almacenados en caché después de un tiempo estableciendo un TTL. En este momento, por lo que puedo ver, necesito borrarlo yo mismo usando el @CacheEvict, y al usarlo junto con @Scheduled, puedo hacer una implementación TTL yo mismo, pero ¿parece un poco demasiado para una tarea tan simple?

Piotr
fuente

Respuestas:

39

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

¿Cómo puedo configurar la función TTL / TTI / Política de desalojo / XXX?

Directamente a través de su proveedor de caché. La abstracción de caché es ... bueno, una abstracción, no una implementación de caché

Entonces, si usa EHCache, use la configuración de EHCache para configurar el TTL.

También puede usar CacheBuilder de Guava para construir un caché y pasar la vista ConcurrentMap de este caché al método setStore de ConcurrentMapCacheFactoryBean .

JB Nizet
fuente
57

Spring 3.1 y Guayaba 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
fuente
21
Para Spring 4.1, extienda CachingConfigurerSupport y solo sobrescriba cacheManager ().
Johannes Flügel
39

A continuación se muestra un ejemplo completo de cómo configurar Guava Cache en Spring. Usé Guava sobre Ehcache porque es un poco más liviano y la configuración me pareció más sencilla.

Importar dependencias de Maven

Agregue estas dependencias a su archivo pom maven y ejecute clean and packages. Estos archivos son los métodos de ayuda de Guava dep y Spring para su uso en 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>

Configurar la caché

Necesita crear un archivo CacheConfig para configurar el caché usando la configuración de 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());
   }
}

Anotar el método que se almacenará en caché

Agregue la anotación @Cacheable y pase el nombre de la caché.

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

Puede ver un ejemplo más completo aquí con capturas de pantalla anotadas: Guava Cache en Spring

anataliocs
fuente
2
Nota: La caché de guayaba ahora está obsoleta en Spring 5 ( stackoverflow.com/questions/44175085/… )
Amin
33

Uso la vida hackear como este

@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
fuente
¿Estás llamando al reportCacheEvictmétodo desde cualquier lugar? ¿Cómo está pasando el cacheEvict?
Jaikrat
Consíguelo. No llamamos a este método desde ninguna parte. Se llama después del intervalo de tiempo FixedDelay. Gracias por la pista.
Jaikrat
1
Limpiar todo el caché en un horario puede ser un truco útil para que las cosas funcionen, pero este método no se puede usar para asignar un TTL a los elementos. Incluso el valor de la condición solo puede declarar si se borra todo el caché. Detrás de esto está el hecho de que ConcurrentMapCache almacena objetos sin ninguna marca de tiempo, por lo que no hay forma de evaluar un TTL tal cual.
jmb
es el código seat-of-the-pants (este método fue garabateado :)).
Atum
Enfoque agradable y limpio
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;
}

}

y

@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}
jo8937
fuente
¡Asombroso! Esto era exactamente lo que estaba buscando
MerLito
6

esto se puede hacer extendiendo org.springframework.cache.interceptor.CacheInterceptor, y anulando el método "doPut" - org.springframework.cache.interceptor.AbstractCacheInvoker tu lógica de anulación debe usar el método put del proveedor de caché que sabe configurar TTL para la entrada de caché (en mi caso uso 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);



}

en su configuración de caché, debe agregar esos 2 métodos de frijol, creando su instancia de interceptor personalizada.

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


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

Esta solución es buena cuando desea configurar el TTL en el nivel de entrada y no globalmente en el nivel de caché

lukass77
fuente
2

Desde Spring-boot 1.3.3, puede establecer el tiempo de expiración en CacheManager usando RedisCacheManager.setExpires o RedisCacheManager.setDefaultExpiration en el bean de devolución de llamada CacheManagerCustomizer .

Andrés
fuente
0

Al usar Redis, TTL se puede configurar en un archivo de propiedades como este:

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

Si está trabajando con redis y Java 8, puede echar un vistazo a JetCache :

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

Huang Li
fuente
1
la pregunta es para la primavera @Anotación en
caché