Кэширование весной с помощью аннотаций EhCache
Опубликовано: 2022-03-11EhCache — это широко используемый кэш Java, который можно легко интегрировать с большинством популярных платформ Java, таких как Spring и Hibernate. Его часто считают наиболее удобным выбором для Java-приложений, поскольку его легко интегрировать в проекты. Особенно:
- Его можно настроить, просто включив JAR в свой проект. Никаких дополнительных действий по установке не требуется.
- Он работает в одном процессе с приложением, поэтому работает быстро. Для запуска не требуется никаких дополнительных служб.
Короче говоря, EhCache — отличный выбор для любого чисто Java-приложения.
Кроме того, EhCache Spring Annotations обеспечивает бесшовную интеграцию в любое приложение Spring, просто добавляя аннотации к кэшируемым методам без изменения реализации методов.
Хотя EhCache предоставляет простые, богатые API-интерфейсы для программного управления кешем, эта статья в основном посвящена ускорению ваших приложений Spring менее навязчивым способом с помощью аннотаций EhCache Spring. Мы настроим проект Spring MVC и развернем веб-службу RESTful в Tomcat. Затем EhCache будет интегрирован в веб-сервис.
Обзор проекта
Мы продемонстрируем аннотации EhCache в контексте примера проекта. Мы настроим веб-службу на основе Spring MVC, размещенную на сервере Tomcat 8.
Я разработал проект в Eclipse, который можно установить, следуя инструкциям здесь.
Последнюю стабильную версию Tomcat, Tomcat 8, можно скачать здесь.
Конечно, эти конкретные платформы не являются обязательными для EhCache; вы всегда можете выбрать свою любимую IDE и сервер.
JAR EhCache Spring Annotations доступен здесь. Как мы видим, для каждой версии есть два JAR-файла: один с зависимостями и один без них. Тот, у которого есть зависимости, также включает EhCache 2 и Spring 3, которые необходимы для работы аннотаций EhCache. Его проще настроить, если мы загрузим файл с зависимостями и добавим его в наш путь сборки.
Аннотации EhCache Spring также совместимы с Spring 4, хотя их необходимо настраивать отдельно. Пока не ясно, будет ли проект поддерживать EhCache 3 в ближайшем будущем. Для тех, кто использует или собирается использовать EhCache 3, подход с аннотациями, обсуждаемый в этой статье, не рекомендуется.
Наконец, мы будем использовать Maven для управления всем. Maven поставляется в комплекте с большинством инсталляций Eclipse, но его также можно получить здесь. Зависимости Spring MVC и EhCache Spring Annotations можно добавить довольно легко, как показано далее в этой статье.
Настройка проекта
Если вы никогда раньше не настраивали проект Spring, вы также можете найти сообщение Stefan Varga на эту тему информативным.
Для этой демонстрации мы настроим базовый проект, используя архетип Maven maven-archetype-webapp . Общая структура файла будет выглядеть так:
Создайте каталог src/main/java с тремя пакетами: com.toptal.blog , com.toptal.blog.cache и com.toptal.blog.service . Исходный код нашего приложения будет находиться в этих пакетах, как описано ниже.
Давайте определим сервлет Tomcat с именем «springrest» в web.xml :
<web-app> ... <servlet> <servlet-name>springrest</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springrest</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> Если явно не указано иное, Spring MVC DispatcherServlet будет искать файл конфигурации XML с именем {servlet-name}-servlet.xml в каталоге WEB-INF . Давайте создадим файл конфигурации с именем springrest-servlet.xml . Чтобы включить методы контроллера процессов Spring, аннотированные с помощью @RequestMapping , давайте просто добавим <mvc:annotation-driven /> в этот файл. Кроме того, давайте определим базовый пакет для Spring для автоматического сканирования и регистрации bean-компонентов, добавив <context:component-scan base-package="com.toptal.blog" /> . springrest-servlet.xml становится следующей:
<beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>Простой веб-сервис RESTful
Теперь, когда наш проект правильно настроен, давайте реализуем простой API службы сообщений. В наш базовый пакет project.toptal.blog мы добавим SpringRestControllerWithEhCache.java с одним методом GET для получения сообщения по идентификатору и одним методом POST для установки сообщения по идентификатору:
@RestController @RequestMapping( "/" ) public class SpringRestControllerWithEhCache { @Autowired MessageService messageService; @RequestMapping( value = "/message/{id}", method = RequestMethod.GET ) public String getMessage( @PathVariable Integer id ) { String message = messageService.getMessage( id ); System.out.println( "get message ["+message+"] at "+new Date() ); return message; } @RequestMapping( value = "/message/set/{id}/{message}", method = RequestMethod.POST ) public String setMessage( @PathVariable Integer id, @PathVariable String message ) { System.out.println( "set message ["+message+"] at "+new Date() ); messageService.setMessage( id, message ); return message; } } Мы определим класс MessageService в com.toptal.blog.service . Он будет получать доступ к сообщениям, хранящимся в нашей Системе записей (SOR). В рабочем приложении SOR будет чем-то вроде реляционной базы данных. Для простоты мы будем использовать HashMap :
@Service public class MessageService { private ConcurrentHashMap<Integer, String> messages = new ConcurrentHashMap<Integer, String>(); public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } public void setMessage( Integer id, String message ){ messages.put( id, message ); } } Теперь, если мы экспортируем проект как WAR и развернем его в Tomcat, мы сможем установить сообщение, например «test_message», для ID=1, создав запрос HTTP POST по адресу http://localhost:8080/EhCacheExample/message/set/1/test_message . Затем мы сможем получить «test_message» с помощью HTTP-запроса GET по адресу http://localhost:8080/EhCacheExample/message/1 . Я использовал Insomnia в качестве удобного REST-клиента для проведения теста.
Подключить аннотации EhCache Spring
Теперь давайте заставим EhCache работать на нас. Требуется всего несколько быстрых шагов, чтобы настроить наш проект для правильной работы EhCache.
Шаг 1. Обновите зависимости для использования аннотаций EhCache Spring
Добавьте зависимость EhCache Spring Annotations в pom.xml Maven:
<!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>Шаг 2. Настройте собственный менеджер кэша
Spring имеет встроенный менеджер кеша EhCache, org.springframework.cache.ehcache.EhCacheManagerFactoryBean . Это подходит для большинства ситуаций кэширования, но я обнаружил, что определение пользовательского менеджера кеша полезно, потому что это позволяет мне управлять кешем либо программно, либо с помощью аннотаций, используя тот же менеджер кеша. В этой статье основное внимание уделяется аннотациям, но давайте продолжим и определим настраиваемый менеджер кеша, чтобы быть готовым на случай, если он нам понадобится. Если вы предпочитаете использовать диспетчер кеша по умолчанию, вы можете пропустить этот шаг.

Мы определим новый класс в com.toptal.blog.cache.CustomCacheManager :
public class CustomCacheManager extends net.sf.ehcache.CacheManager{ public CustomCacheManager(){ super(); } /* Add your own cache methods here. * * public void myCustomCacheMethod(){ * // your code here * } * */ } Включите его, обновив springrest-servlet.xml следующим образом:
... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...Шаг 3. Настройте EhCache
Наконец, создайте файл конфигурации ehcache.xml в пути к классам. По умолчанию Eclipse включит src/main/resources в путь к классам, и мы поместим файл сюда. Этот файл необходим для правильной работы EhCache. Он определяет имена кэшей и некоторые свойства каждого кэша, такие как timeToLiveSeconds :
<ehcache xmlms:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <diskStore path="cache" /> <cache name="messageCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="10" overflowToDisk="false" memoryStoreEvictionPolicy="LFU" /> </ehcache>Шаг 4. Проверьте кэш
Теперь, когда все настроено и готово к работе, использование EhCache должно стать легкой и приятной работой. Мы можем просто добавить @Cacheable к методу или классу, который мы хотим кэшировать. Например, я добавил @Cacheable в метод getMessage в MessageService . Это так просто!
@Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } Чтобы проверить, работает ли наш кеш, мы можем создать сообщение для ID=1, выполнив HTTP-запрос POST по адресу http://localhost:8080/EhCacheExample/message/set/1/newMessage , а затем получить сообщение для ID= 1 несколько раз с запросами GET на http://localhost:8080/EhCacheExample/message/1 . Как вы можете видеть в выводе консоли ниже, веб-служба просит SOR получить сообщение при первом запросе сообщения, но не для следующих двух запросов, вместо этого возвращая кешированное сообщение. Поскольку мы определили timeToLiveSeconds равным 10, веб-служба вызывает SOR, чтобы снова получить сообщение через 10 секунд:
set message [newMessage] at Sun Dec 06 23:55:39 MST 2015 get message [newMessage] at Sun Dec 06 23:55:42 MST 2015 Getting data from SOR...... get message [newMessage] at Sun Dec 06 23:55:47 MST 2015 get message [newMessage] at Sun Dec 06 23:55:49 MST 2015 get message [newMessage] at Sun Dec 06 23:55:54 MST 2015 Getting data from SOR......Обновление кеша
Теперь мы наслаждаемся скоростью и удобством, которые дает нам кеш, и EhCache достаточно удобен, чтобы обновляться каждые 10 секунд. Но что, если мы хотим, чтобы он обновлялся сразу после обновления нашего SOR? EhCache Spring Annotation предлагает @TriggersRemove для удаления указанных ключей из кеша при вызове аннотированного метода. В нашем API службы сообщений кэшированное сообщение должно быть удалено из кеша при setMessage . Таким образом, при следующем поступлении запроса getMessage кеш будет получать свежую запись из SOR:
@Cacheable( cacheName = "messageCache", keyGenerator = @KeyGenerator ( // method name is not included in cache key to work with @TriggersRemove name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } @TriggersRemove( cacheName = "messageCache", keyGenerator = @KeyGenerator ( name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public void setMessage( @PartialCacheKey Integer id, String message ) { messages.put( id, message ); } Генератор ключей используется диспетчером кэша для генерации ключа кэша. Список предопределенных генераторов ключей кэша можно найти здесь. По умолчанию @KeyGenerator как имя метода, так и переданные параметры для создания ключа кэша. Но поскольку мы хотим, чтобы метод setMessage генерировал тот же ключ, что и getMessage , и удалял кэшированное значение, связанное с этим ключом, мы должны использовать только идентификатор сообщения в качестве ключа и исключить имя метода для генерации ключа. Поэтому мы устанавливаем для свойства includeMethod генератора ключей значение false для обоих методов. Кроме того, поскольку setMessage имеет два аргумента, мы используем аннотацию EhCache @PartialCacheKey к параметру id , чтобы указать, что это единственный параметр, который должен использоваться генератором ключей. Наконец, вспомните, что мы настроили выделенный кеш messageCache для этого типа ресурса, поэтому использование только идентификатора ключа не представляет опасности конфликтов с другими типами ресурсов.
Теперь, если мы сделаем несколько HTTP-запросов для сообщения с ID=1, следующим образом:
HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage1 HTTP GET:http://localhost:8080/EhCacheExample/message/1 HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage2 HTTP GET:http://localhost:8080/EhCacheExample/message/1Консоль покажет:
set message [newMessage1] at Tue Dec 08 17:53:44 MST 2015 get message [newMessage1] at Tue Dec 08 17:53:47 MST 2015 Getting data from SOR...... set message [newMessage2] at Tue Dec 08 17:53:50 MST 2015 get message [newMessage2] at Tue Dec 08 17:53:53 MST 2015 Getting data from SOR......Заключение
Окончательная структура проекта выглядит так:
В этом примере мы сначала создали простое веб-приложение Spring MVC RESTful. Не изменяя ни одной строки существующего кода приложения, мы легко интегрировали EhCache в приложение с помощью аннотаций EhCache Spring. Мы продемонстрировали, что EhCache Spring Annotations легко установить (путем добавления его зависимости от Maven) и элегантно использовать (путем добавления аннотаций к методам).
Дальнейшее чтение
Документацию EhCache можно найти здесь, а документацию EhCache Spring Annotations — здесь.
Кроме того, ознакомьтесь с примером проекта, описанным в этой статье, на GitHub.
