2012-04-27 13 views
25

Sto cercando di implementare la cache di Spring 3.1 come spiegato here e here, ma non sembra funzionare: il mio metodo viene eseguito ogni volta anche se è contrassegnato con @cacheable . Che cosa sto facendo di sbagliato?Spring 3.1 @Cacheable - metodo ancora eseguito

L'ho spostato in un caso di test di junit con un proprio file di configurazione per isolarlo dal resto della mia applicazione, ma il problema si verifica ancora. Qui ci sono i file rilevanti:

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

rilevanti: record Pom: (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> 

quando si esegue il test, Primavera mette fuori alcuni messaggi di debug che assomiglia la cache viene inizializzata senza errori

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

ma l'output di debug non mostra i controlli della cache tra le chiamate di metodo al SomeMethod e l'istruzione print da inside someMethod prints every time.

C'è qualcosa che mi manca?

risposta

76

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

In modalità proxy (che è il default), unico metodo esterno chiama proveniente attraverso il proxy vengono intercettate. Ciò significa che auto-invocazione, in effetti, un metodo nell'oggetto bersaglio chiamata altro metodo dell'oggetto bersaglio, non porterà ad un effettivo caching in fase di esecuzione, anche se il metodo richiamato è contrassegnata con @ In cache - considerando l'utilizzo della modalità aspectj in questo caso.

e

visibilità Metodo e @Cacheable/@CachePut/@CacheEvict

Quando si utilizza proxy, si dovrebbe applicare le @Cache annotazioni solo per metodi con visibilità pubblica.

  1. È sé richiama someMethod nello stesso oggetto di destinazione.
  2. Il tuo metodo @Cacheable non è pubblico.
+0

In aggiunta a questo, è necessario per estrarre su un'interfaccia in modo che Spring possa scrivere dinamicamente un wrapper per la tua classe. – RockMeetHardplace

2

È necessario definire una cache che corrisponda al nome a cui si fa riferimento nell'annotazione ("testmethod"). Crea anche una voce nel tuo ehcache.xml per quella cache.

+0

Questo è stato anche un problema con il mio codice originale, che è apparsa evidente una volta che ho chiamato il metodo in un modo che ha cercato di metterlo nella cache (vedi soluzione accettata) –

1

Oltre a Lee Chee Kiam: Ecco la mia soluzione per piccoli progetti con utilizzo solo marginale delle chiamate di metodo di bypass (non annotate). Il DAO viene semplicemente iniettato in se stesso come proxy e chiama i propri metodi utilizzando quel proxy invece di una semplice chiamata di metodo. Quindi @Cacheable è considerato senza complicazioni.

La documentazione in codice è fortemente consigliata, in quanto potrebbe sembrare strana ai colleghi. Ma è facile da testare, semplice, veloce da raggiungere e mi risparmia la strumentazione completa AspectJ. Tuttavia, per un uso più intenso, consiglierei anche la soluzione AspectJ come fece 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

è solo l'annotazione @Scope che ignora la modalità proxy? Ho una domanda simile qui http://stackoverflow.com/questions/36486620/correct-key-annotation-for-caching-on-items-in-a-list-rather-then-the-whole-list –

+0

I ' scusa, penso di non aver capito bene la domanda. Non vedo la connessione al tuo post. –

+0

"bypassare" in quel contesto significa che il metodo non è annotato con @Cachable MA utilizza internamente i metodi chached del proxy immesso, CHE a sua volta è un proxy di "this" itslef. Un po 'strano;) L'oggetto è iniettato in se stesso come proxy per evitare complicate operazioni di installazione. –

Problemi correlati