19 ноября 2024
Тэги: Java, maven, rest, Spring Boot.
Spring Boot поддерживает простой механизм кеширования данных. Рассмотрим его на примере, исходники которого доступны на github.
Создадим стандартное приложение Spring Boot. Это удобно делать через Spring Initializr.
Добавим в pom.xml две зависимости:
Зависимость spring-boot-starter-web – это базовая функциональность нашего веб-приложения, в том числе поддержка rest-контроллеров. Зависимость spring-boot-starter-cache добавляет возможность кеширования.
Предположим, что мы хотим кешировать некие записи, представленные классом MyRecord. Очевидно, что в последних версиях Java это должен быть не просто класс, а record-класс. То есть класс, предназначенный для хранения данных. Все поля доступа и служебные методы для него компилятор генерирует автоматически.
MyRecord содержит всего лишь два поля: числовой идентификатор и время создания для наглядной демонстрации изменения состояния кеша.
Предположим, что извлечение этих данных без кеширования является дорогой по времени операцией. Для этого создадим сервис, который будет имитировать «тяжёлое» обращение к хранилищу с данными. Именно это обращение мы будем затем кешировать.
В реальных проектах обращение к БД, либо к удалённому сервису может быть очень долгим, поэтому имеет смысл кешировать редко изменяющиеся объекты.
Далее добавляем в сервис четыре метода: getOrCreateRecord(), createOrUpdateRecord(), deleteRecord(), deleteAllRecords().
Метод getOrCreateRecord() возвращает объект по его id. Имитация «тяжёлого» обращения к БД делается с помощью метода TimeUnit.SECONDS.sleep(3), который приостанавливает выполнение потока на 3 секунды. Затем просто создаём новый объект и возвращаем его как результат. Аннотация @Cacheable говорит, что возвращаемый результат необходимо положить в кеш, если его там нет. А если он там есть, то просто вернуть значение из кеша. Параметр cacheNames указывает на имя кеша, а key указывает параметр метода, по значению которого проверять наличие элемента в кеше. Обратите внимание на символ «#», стоящий перед именем параметра.
При первом обращении к методу вы увидите задержку в 3 секунды, а затем получите в ответ созданный объект. При последующих обращениях с тем же id результат будет возвращаться мгновенно, т.к. запись будет браться из кеша и дата создания объекта меняться не будет.
Метод createOrUpdateRecord() создаёт новый объект MyRecord и возвращает его в качестве результата. Аннотация @CachePut принудительно помещает объект, возвращаемый данным методом, в кеш. Если в кеше по данному ключу уже был объект, он будет перезаписан. Имя кеша, аналогично предыдущей аннотации, указывается в параметре cacheNames, а имя параметра метода, по значению которого следует искать запись в кеше – через параметр key.
Каждый раз после выполнения этого метода в кеше будут обновляться записи.
Метод deleteRecord() сам ничего не делает. Однако аннотация @CacheEvict удаляет запись из кеша по указанному ключу recordId. После выполнения данного метода в кеше будет удаляться одна запись.
Однако иногда бывает полезно сбросить весь кеш целиком, т.е. удалить разом все записи. Для этого также будем использовать аннотацию @CacheEvict с флагом allEntries.
Теперь создадим простой RestController, чтобы можно было вызывать наши методы.
При запуске приложения данный контроллер по умолчанию будет доступен по адресу 127.0.0.1:8080. Метод getOrCreateRecord вызывается с помощью GET-запроса по адресу 127.0.0.1:8080/{id}, где id – номер нашей записи. Аналогично метод createOrUpdateRecord вызывается PUT-запросом по тому же адресу с указанием id записи. Метод deleteRecord – DELETE-запросом с указанием id. А метод deleteAllRecords – тоже DELETE-запросом, но без указания id.
Для активации механизма кеширования не забудьте добавить аннотацию @EnableCaching в основной класс нашего приложения, туда, где находится стандартный метод main() и аннотация @SpringBootApplication.
Запускаем приложение и делаем GET-запрос на 127.0.0.1:8080/1. Мы наблюдаем долгий ответ, т.е. для id = 1 объекта в кеше ещё нет. Повторные запросы выполняются мгновенно, а ответ возвращается один и тот же. Далее делаем PUT-запрос на тот же адрес. Объект будет создан и принудительно помещён в кеш. Ещё раз выполняем тот же GET-запрос и наблюдаем быстрый ответ, который вернёт нам сущность, созданную методом PUT. Теперь удалим эту запись, выполнив DELETE-запрос с указанием этого id. И затем ещё раз GET-запрос – он опять ответит с задержкой. То есть после DELETE записи в кеше не было.
Для проверки сброса всего кеша, можно выполнить несколько GET-запросов с разным id. У нас в кеш будет помещено несколько записей. Затем вызовем DELETE без id – и это приведёт к удалению всех записей из кеша. Ещё раз вызовем GET-запрос с указанием ранее созданного id – и снова будем наблюдать задержку в 3 секунды. То есть кеш был очищен корректно.
Kotlin, Java, Spring, Spring Boot, Spring Data, SQL, PostgreSQL, Oracle, Linux, Hibernate, Collections, Stream API, многопоточность, файлы, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.
16.04.2022 09:30 Михаил
Спасибо!)
19.11.2024 21:57 devmark
Обновил статью про кеширование запросов в Spring. Также перевёл пример на github на Spring Boot 3 и Java 21.