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, H2, Linux, Hibernate, Collections, Stream API, многопоточность, чат-боты, нейросети, файлы, devops, Docker, 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.