2012-04-27 10 views
25

Próbuję zaimplementować buforowanie w Spring 3.1, jak wyjaśniono here i here, ale to nie działa: moja metoda jest uruchamiana za każdym razem, chociaż jest oznaczona jako @cacheable . Co ja robię źle?Wiosna 3.1 @Cacheable - metoda nadal wykonywana

Przeniosłem to na przypadek testowy ze wspólnym plikiem konfiguracyjnym, aby odizolować go od reszty aplikacji, ale problem nadal występuje. Oto odpowiednie pliki:

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

Istotne wpisy Pom (wiosna -wersja = 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> 

kiedy uruchomić test, Wiosna wypuszcza jakieś komunikaty debugowania, który wygląda jak moja pamięć podręczna jest inicjowany bez błędów

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

ale wyjście debugowania nie wykazuje żadnych czeków cache metoda pomiędzy wzywa do SomeMethod i instrukcja print z wewnątrz someMethod drukuje za każdym razem.

Czy jest coś, czego mi brakuje?

Odpowiedz

76

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

W trybie proxy (który jest domyślny), tylko metoda zewnętrznych wymaga wpadającym przez pełnomocnika są przechwytywane. Oznacza to, że samo wezwanie, w efekcie, metoda wewnątrz obiektu docelowego wywołującego inna metoda obiektu docelowego, nie doprowadzi do rzeczywistej buforowania w czasie wykonywania nawet jeśli wywołana metoda oznaczonego @ Cacheable - biorąc pod uwagę użycie trybu aspectj w tym przypadku.

i

widoczność Sposób i @Cacheable/@CachePut/@CacheEvict

Podczas używania serwerów proxy, należy zastosować @Cache adnotacje tylko metod z widoczności publicznej.

  1. Ty własnym powołać someMethod w tym samym obiekcie docelowym.
  2. Twoja metoda @Cacheable nie jest publiczna.
+0

Poza tym, trzeba wyodrębnić do interfejsu, aby Spring mógł dynamicznie pisać opakowanie do klasy. – RockMeetHardplace

2

Musisz zdefiniować pamięć podręczną, która pasuje do nazwy, do której się odwołujesz, do adnotacji ("testmethod"). Utwórz także wpis w pliku ehcache.xml dla tej pamięci podręcznej.

+0

Był również problem z moim oryginalnego kodu, który stał się oczywisty raz zadzwoniłem metodę w sposób, który próbuje umieścić go w pamięci podręcznej (patrz przyjętego rozwiązania) –

1

Oprócz Lee Chee Kiam: Oto moje rozwiązanie dla małych projektów z marginalnym wykorzystaniem obejścia (nie annotowanych) wywołań metod. DAO jest po prostu wstrzykiwany do siebie jako serwer proxy i wywołuje swoje metody za pomocą tego serwera proxy zamiast zwykłego wywołania metody. Tak więc @Cacheable jest uważany za bez skomplikowanej insturmentation.

Dokumentacja In-Code jest zdecydowanie zalecana, ponieważ może wyglądać dziwnie dla kolegów. Ale jest łatwy do przetestowania, prosty, szybki do osiągnięcia i zapewnia pełną funkcjonalność oprzyrządowania AspectJ. Jednakże, dla bardziej intensywnego użytkowania, radziłbym także rozwiązania AspectJ, tak jak zrobił to 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

czy to tylko adnotacja @Scope, która omija tryb proxy? Mam podobne pytanie tutaj http://stackoverflow.com/questions/36486620/correct-key-annotation-for-caching-on-items-in-a-list-rather-then-ten-whole-list –

+0

I ' Przepraszam, myślę, że nie dostałem tego pytania. Nie widzę połączenia z Twoim wpisem. –

+0

"omijanie" w tym kontekście oznacza, że ​​metoda nie jest opatrzona adnotacją @Cachable ALE wewnętrznie używa gotowych metod wstrzykiwania proxy, KTÓRY Z kolei jest pośrednikiem "tego" jego adresu. Kinda freaky;) Obiekt jest wstrzykiwany w siebie jako proxy, aby uniknąć skomplikowanej instumentacji. –