В этой статье я расскажу как можно кэшировать в memcache значения, возвращаемые методами bean'ов. Для этого не потребуется писать код, достаточно добавить конфигурации в xml файлы Spring'а и разметить код с помощью аннотаций.
Итак поредставим что у нас есть DAO с тремя методами — двумя для поиска сущностей и одним для сохранения сущностей. Для правильного функционирования нам нужен интерфейс и реализующий его класс (чтобы можно было добавить dynamic proxy на реализацию)
И описание bean'а в конфигурации Spring'а
Теперь нам нужен какой-то компонент, который будет обрабатывать аннотации и сливать значения метода в кэш. Когда Spring был еще второй версии, то для него существовала библиотека spring-modules, которая позволяла это сделать. Проект был заброшен на какое-то время, но нашлись люди, которые стали развивать проект на github и их трудами мы воспользуемся.
Необходимо скачать последнюю версию исходников отсюда http://github.com/abashev/spring-modules и собрать их.
А теперь начинается уличная магия :) Добавляем аннотации в реализацию DAO
Добавляем менеджер кэша в Spring context
Объект cacheFacade используется для создания кэшей и настройки их параметров. Для того чтобы добавить специальные свойства для кэшей (для GAE это может быть настройка namespace и expire) необходимо переопределить свойство «cacheProperties».
Для того Spring корректно обработал аннотации необходимы три bean'а — один attribute collector, один caching interception и один caching advisor. Для обработки аннотации @Cachable
Для обработки @CacheFlush
И последний штрих
Итак поредставим что у нас есть DAO с тремя методами — двумя для поиска сущностей и одним для сохранения сущностей. Для правильного функционирования нам нужен интерфейс и реализующий его класс (чтобы можно было добавить dynamic proxy на реализацию)
public interface SampleDAO {
public Sample findById(int id);
public Sample findByUrl(String url);
public void save(Sample sample);
}
public class SampleDAOImpl implements SampleDAO {
public Sample findById(int id) { /* какой-то код */ }
public Sample findByUrl(String url) { /* какой-то код */ }
public void save(Sample sample) { /* какой-то код */ }
}
* This source code was highlighted with Source Code Highlighter.
И описание bean'а в конфигурации Spring'а
<bean id="sampleDAO" class="SampleDAOImpl">
<property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>
* This source code was highlighted with Source Code Highlighter.
Теперь нам нужен какой-то компонент, который будет обрабатывать аннотации и сливать значения метода в кэш. Когда Spring был еще второй версии, то для него существовала библиотека spring-modules, которая позволяла это сделать. Проект был заброшен на какое-то время, но нашлись люди, которые стали развивать проект на github и их трудами мы воспользуемся.
Необходимо скачать последнюю версию исходников отсюда http://github.com/abashev/spring-modules и собрать их.
$ git clone git://github.com/abashev/spring-modules.git
$ cd spring-modules/projects
$ mvn install // Тут будет много ошибок, но нужный xml файл установится
$ cd spring-modules-cache
$ mvn install -DskipTests
* This source code was highlighted with Source Code Highlighter.
А теперь начинается уличная магия :) Добавляем аннотации в реализацию DAO
public class SampleDAOImpl {
@Cacheable(modelId = "findByOldId")
public Sample findById(int id) { /* какой-то код */ }
@Cacheable(modelId = "findByUrl")
public Sample findByUrl(String url) { /* какой-то код */ }
@CacheFlush(modelId = "save")
public void save(Sample sample) { /* какой-то код */ }
}
* This source code was highlighted with Source Code Highlighter.
Добавляем менеджер кэша в Spring context
<!-- Cache manager for Jsr107 cache -->
<bean id="cacheManager" class="org.springmodules.cache.provider.jsr107.Jsr107CacheManagerFactoryBean"/>
* This source code was highlighted with Source Code Highlighter.
Объект cacheFacade используется для создания кэшей и настройки их параметров. Для того чтобы добавить специальные свойства для кэшей (для GAE это может быть настройка namespace и expire) необходимо переопределить свойство «cacheProperties».
<bean id="cacheProviderFacade" class="org.springmodules.cache.provider.jsr107.Jsr107CacheFacade">
<property name="cacheManager" ref="cacheManager" />
<property name="cacheProperties">
<map>
<entry key="findByOldId">
<map>
<entry key="com.google.appengine.api.memcache.jsr107cache.NAMESPACE" value="findByOldId"/>
</map>
</entry>
<entry key="findByUrl">
<map>
<entry key="com.google.appengine.api.memcache.jsr107cache.NAMESPACE" value="findByUrl"/>
</map>
</entry>
</map>
</property>
</bean>
* This source code was highlighted with Source Code Highlighter.
Для того Spring корректно обработал аннотации необходимы три bean'а — один attribute collector, один caching interception и один caching advisor. Для обработки аннотации @Cachable
<bean id="cachingAttributeSource" class="org.springmodules.cache.annotations.AnnotationCachingAttributeSource"/>
<bean id="cachingInterceptor" class="org.springmodules.cache.interceptor.caching.MetadataCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingAttributeSource" ref="cachingAttributeSource" />
<property name="cachingModels">
<map>
<!-- Установка соответствия между значением modelId в аннотации и именем кэша -->
<entry key="findByOldId">
<bean class="org.springmodules.cache.provider.jsr107.Jsr107CacheCachingModel">
<constructor-arg value="findByOldId"/>
</bean>
</entry>
<entry key="findByUrl">
<bean class="org.springmodules.cache.provider.jsr107.Jsr107CacheCachingModel">
<constructor-arg value="findByUrl"/>
</bean>
</entry>
</map>
</property>
</bean>
<bean id="cachingAttributeSourceAdvisor" class="org.springmodules.cache.interceptor.caching.CachingAttributeSourceAdvisor">
<constructor-arg ref="cachingInterceptor" />
</bean>
* This source code was highlighted with Source Code Highlighter.
Для обработки @CacheFlush
<bean id="flushingAttributeSource" class="org.springmodules.cache.annotations.AnnotationFlushingAttributeSource"/>
<bean id="flushingInterceptor" class="org.springmodules.cache.interceptor.flush.MetadataFlushingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="flushingAttributeSource" ref="flushingAttributeSource" />
<property name="flushingModels">
<map>
<!-- Установка соответствия между значением modelId и именем кэша -->
<entry key="save">
<bean class="org.springmodules.cache.provider.jsr107.Jsr107CacheFlushingModel">
<constructor-arg value="findByOldId, findByUrl"/>
</bean>
</entry>
</map>
</property>
</bean>
<bean id="flushingAttributeSourceAdvisor" class="org.springmodules.cache.interceptor.flush.FlushingAttributeSourceAdvisor">
<constructor-arg ref="flushingInterceptor" />
</bean>
* This source code was highlighted with Source Code Highlighter.
И последний штрих
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
* This source code was highlighted with Source Code Highlighter.