web-dev-qa-db-fra.com

Spring 3.1 @Cacheable - méthode toujours exécutée

J'essaie d'implémenter la mise en cache Spring 3.1 comme expliqué ici et ici , mais cela ne semble pas fonctionner: ma méthode est exécutée à chaque fois même si elle est marqué @cacheable. Qu'est-ce que je fais mal?

Je l'ai déplacé dans un cas de test junit avec son propre fichier de configuration pour l'isoler du reste de mon application, mais le problème persiste. Voici les fichiers pertinents:

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

Entrées Pom pertinentes: (version printemps = 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>

lorsque j'exécute le test, Spring affiche des messages de débogage qui semblent que mon cache est initialisé sans erreurs

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'.

mais la sortie de débogage ne montre aucune vérification du cache entre les appels de méthode à someMethod et l'instruction print de l'intérieur de someMethod s'imprime à chaque fois.

Y a-t-il quelque chose qui me manque?

28
Andy Miller

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

En mode proxy (qui est la valeur par défaut), seuls les appels de méthode externes entrant via le proxy sont interceptés. Cela signifie que l'auto-invocation, en effet, une méthode dans l'objet cible appelant une autre méthode de l'objet cible , ne conduira pas à une mise en cache réelle au moment de l'exécution même si la méthode invoquée est marquée avec @Cacheable - considérant l'utilisation du mode aspectj dans ce cas.

et

Visibilité de la méthode et @Cacheable/@CachePut/@CacheEvict

Lorsque vous utilisez des proxys, vous devez appliquer le @Cache annotations uniquement aux méthodes avec visibilité publique .

  1. Vous appelez automatiquement someMethod dans le même objet cible.
  2. Votre @Cacheable la méthode n'est pas publique.
80
Lee Chee Kiam

Vous devez définir un cache correspondant au nom auquel vous faites référence dans votre annotation ("méthode de test"). Créez également une entrée dans votre ehcache.xml pour ce cache.

2
pap

En plus de Lee Chee Kiam: Voici ma solution pour les petits projets avec seulement une utilisation marginale des appels de méthode de contournement (non annotés). Le DAO est simplement injecté en lui-même en tant que proxy et appelle ses propres méthodes en utilisant ce proxy au lieu d'un simple appel de méthode. Donc, @Cacheable est considéré sans faire d'insturmentation compliquée.

La documentation en code est fortement conseillée, car elle peut sembler étrange à des collègues. Mais c'est facile à tester, simple, rapide à réaliser et m'épargne la pleine instrumentation AspectJ. Cependant, pour un usage plus intensif, je conseillerais également la solution AspectJ comme 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
Mario Eis