Spring 3.1 @ Cacheable-método todavía ejecutado


Estoy tratando de implementar Spring 3.1 caching como se explica aquí y aquí, pero no parece estar funcionando: mi método se ejecuta cada vez a pesar de que está marcado como @cacheable. ¿Qué estoy haciendo mal?

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 sigue ocurriendo. Aquí están los relevantes archivos:

Spring-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 relevantes: (spring-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, Spring emite algunos mensajes de depuración que parecen que mi caché se inicializa 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 la salida de depuración no muestra comprobaciones de caché entre las llamadas al método someMethod y la instrucción print desde dentro de someMethod prints cada vez.

¿Existe algo que me estoy perdiendo?

Author: Andy Miller, 2012-04-27

3 answers

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

En modo proxy (que es el predeterminado), solo llamadas a métodos externos a través del proxy son interceptados. Esto significa que auto-invocación, en efecto, un método dentro del objeto de destino que llama otro método del objeto de destino, no dará lugar a un real caching 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 del método y @Cacheable/@CachePut/@CacheEvict

Al usar proxies, debe aplicar las anotaciones @Cache solo a métodos con visibilidad pública.

  1. Se invoca automáticamente someMethod en el mismo objeto de destino.
  2. Su método @Cacheable no es público.
 80
Author: Lee Chee Kiam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-29 08:09:07

Necesita definir una caché que coincida con el nombre al que hace referencia en su anotación ("testmethod"). Crea una entrada en tu ehcache.xml para esa caché también.

 2
Author: pap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-27 08:08:42

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

La documentación en el código está fuertemente indicada, ya que puede parecer extraña para los colegas. Pero es fácil de probar, simple, rápido de lograr y me ahorra toda la instrumentación de AspectJ. Sin embargo, para un uso más pesado 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;
    }
}
 1
Author: Mario Eis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-08 20:16:13