Spring 3.1 @Cacheable - метод все еще выполняется
Я пытаюсь реализовать кэширование Spring 3.1, как описано здесь и здесь, но, похоже, он не работает: мой метод запускается каждый раз, даже если он помечен как @cacheable. Что я делаю неправильно?
Я переместил его в тестовый файл junit с собственным конфигурационным файлом, чтобы изолировать его от остальной части моего приложения, но проблема все еще происходит. Вот соответствующие файлы:
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;
}
}
Соответствующие записи Pom: (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>
когда я запускаю тест, Spring выделяет некоторые отладочные сообщения, которые выглядят так, как мой кеш инициализируется без ошибок
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'.
но вывод отладки не показывает проверки кеша между вызовами метода someMethod и оператором печати изнутри someMethod печатает каждый раз.
Есть ли что-то, что мне не хватает?
Ответы
Ответ 1
От http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/cache.html
В режиме прокси (который по умолчанию), только внешние вызовы метода входящие через прокси-сервер, перехватываются. Это значит, что self-invocation, по сути, метод в вызове целевого объекта другой метод целевого объекта, не приведет к фактическому кэширование во время выполнения, даже если вызываемый метод помечен @Cacheable - учитывая использование режима aspectj в этом случае.
и
Видимость метода и @Cacheable
/@CachePut
/@CacheEvict
При использовании прокси следует применять аннотации @Cache
только для методы с общедоступной видимостью.
- Вы сами вызываете
someMethod
в том же целевом объекте.
- Ваш метод
@Cacheable
не является общедоступным.
Ответ 2
Вам нужно определить кеш, который соответствует имени, на которое вы ссылаетесь в аннотации ( "testmethod" ). Создайте запись в вашем ehcache.xml для этого кеша.
Ответ 3
В дополнение к Lee Chee Kiam: Вот мое решение для небольших проектов с ограниченным использованием обходных (не аннотированных) вызовов метода. DAO просто вводится в себя как прокси-сервер и называет его собственными методами, используя этот прокси вместо простого вызова метода. Так что @Cacheable рассматривается без сложной процедуры.
Внутрикодовая документация сильно обостряется, так как это может показаться странным для коллег. Но его легко проверить, просто, быстро достичь и избавит меня от полномасштабного инструментария AspectJ. Однако для более интенсивного использования я также посоветовал решение AspectJ, как это сделал Ли Чи Кьям.
@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;
}
}