2012-04-27 9 views
25

Estoy tratando de implementar el almacenamiento en memoria caché de Spring 3.1 como se explica here y here, pero no parece estar funcionando: mi método se ejecuta siempre aunque esté marcado @cacheable . ¿Qué estoy haciendo mal?Spring 3.1 @Cacheable - método todavía ejecutado

Lo he movido a un caso de prueba junit con su propio archivo de configuración para aislarlo del resto de mi aplicación, pero el problema aún ocurre. Aquí están los archivos pertinentes:

primavera-test-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:cache="http://www.springframework.org/schema/cache" 
    xmlns:p="http://www.springframework.org/schema/p" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> 
<cache:annotation-driven /> 

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/> 
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" 
     p:config-location="classpath:ehcache.xml"/> 
</beans> 

ehcache.xml

<ehcache> 
<diskStore path="java.io.tmpdir"/> 
<cache name="cache" 
     maxElementsInMemory="100" 
     eternal="false" 
     timeToIdleSeconds="120" 
     timeToLiveSeconds="120" 
     overflowToDisk="true" 
     maxElementsOnDisk="10000000" 
     diskPersistent="false" 
     diskExpiryThreadIntervalSeconds="120" 
     memoryStoreEvictionPolicy="LRU"/> 

</ehcache> 

MyTest.java

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"classpath:spring-test-servlet.xml"}) 
@Component 
public class MyTest extends TestCase { 

    @Test 
    public void testCache1(){ 
     for(int i = 0; i < 5; i++){ 
      System.out.println("Calling someMethod..."); 
      System.out.println(someMethod(0)); 
     } 
    } 

    @Cacheable("testmethod") 
    private int someMethod(int val){ 
     System.out.println("Not from cache"); 
     return 5; 
    } 
} 

entradas Pom pertinentes: (primavera -version = 3.1.1.RELEASE)

<dependency> 
     <groupId>net.sf.ehcache</groupId> 
     <artifactId>ehcache-core</artifactId> 
     <version>2.5.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context-support</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-core</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-test</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 

cuando corro la prueba, Primavera pone a cabo algunos mensajes de depuración que se parece a mi caché se inicia sin errores

DEBUG: config.ConfigurationHelper - No CacheManagerEventListenerFactory class specified. Skipping... 
DEBUG: ehcache.Cache - No BootstrapCacheLoaderFactory class specified. Skipping... 
DEBUG: ehcache.Cache - CacheWriter factory not configured. Skipping... 
DEBUG: config.ConfigurationHelper - No CacheExceptionHandlerFactory class specified. Skipping... 
DEBUG: store.MemoryStore - Initialized net.sf.ehcache.store.MemoryStore for cache 
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.data 
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.index 
DEBUG: disk.DiskStorageFactory - Matching data file missing (or empty) for index file. Deleting index file /var/folders/qg/xwdvsg6x3mx_z_rcfvq7lc0m0000gn/T/cache.index 
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.index 
DEBUG: ehcache.Cache - Initialised cache: cache 
DEBUG: config.ConfigurationHelper - CacheDecoratorFactory not configured. Skipping for 'cache'. 
DEBUG: config.ConfigurationHelper - CacheDecoratorFactory not configured for defaultCache. Skipping for 'cache'. 

pero el resultado de la depuración muestra ninguna comprobación de la caché entre el método llama a algunMetodo y la declaración de impresión desde dentro de algoMétodo imprime todo el tiempo.

¿Hay algo que me falta?

Respuesta

76

De http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/cache.html

En el modo de proxy (que es el valor por defecto), sólo el método externo llama que entra a través del proxy son interceptados. Esto significa que auto-invocación, en efecto, un método en el objeto de destino llamando otro método del objeto de destino, no dará lugar a una almacenamiento en caché real en tiempo de ejecución, incluso si el método invocado está marcado con @ Cacheable - considerando usar el modo aspectj en este caso.

y

visibilidad Método y @Cacheable/@CachePut/@CacheEvict

Al utilizar servidores proxy, debe aplicar las anotaciones @Cache sólo para métodos con visibilidad pública.

  1. Usted auto-invoca someMethod en el mismo objeto de destino.
  2. Su método @Cacheable no es público.
+0

Además de esto, usted necesita extraer a una interfaz para que Spring pueda escribir dinámicamente un contenedor para su clase. – RockMeetHardplace

2

Debe definir un caché que coincida con el nombre al que hace referencia en su anotación ("testmethod"). Cree una entrada en su ehcache.xml para ese caché también.

+0

Este fue también un problema con mi código original, que se hizo evidente una vez que se llama el método de una manera que trató de ponerlo en la memoria caché (ver solución aceptada) –

1

Además de Lee Chee Kiam: Aquí está mi solución para pequeños proyectos con solo el uso marginal de llamadas de método de anulación (no anotadas). El DAO simplemente se inyecta en sí mismo como un proxy y llama a sus propios métodos utilizando ese proxy en lugar de una simple llamada a método. Así que se considera @Cacheable sin hacer una complicada insturmentación.

La documentación en el código está muy cargada, ya que puede parecer extraño para los colegas. Pero es fácil de probar, simple, rápido de lograr y me ahorra la instrumentación AspectJ completa. Sin embargo, para un uso más intenso también aconsejaría la solución AspectJ como lo hizo Lee Chee Kiam.

@Service 
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
class PersonDao { 

    private final PersonDao _personDao; 

    @Autowired 
    public PersonDao(PersonDao personDao) { 
     _personDao = personDao; 
    } 

    @Cacheable(value = "defaultCache", key = "#id") 
    public Person findPerson(int id) { 
     return getSession().getPerson(id); 
    } 

    public List<Person> findPersons(int[] ids) { 
     List<Person> list = new ArrayList<Person>(); 
     for (int id : ids) { 
      list.add(_personDao.findPerson(id)); 
     } 
     return list; 
    } 
} 
+0

¿es solo la anotación @Scope que está pasando por alto el modo proxy? Tengo una pregunta similar aquí http://stackoverflow.com/questions/36486620/correct-key-annotation-for-caching-on-items-in-a-list-rather-then-the-whole-list –

+0

I ' Lo siento, creo que no entendí bien la pregunta. No veo la conexión a tu publicación. –

+0

"puentear" en ese contexto significa que el método no está anotado con @Cachable, PERO usa los métodos del proxy inyectado internamente, QUE a su vez es un proxy de "esto" itslef. Un poco estrafalario;) El objeto se inyecta en sí mismo como un proxy para evitar la instumentación complicada. –

Cuestiones relacionadas