Spring @Cacheable: нужно знать, возвращается значение из кэша или нет

java #spring #spring-aop #spring-cache

#java #весна #весна-аоп #spring-cache

Вопрос:

Наши приложения используют Spring Cache и должны знать, был ли ответ возвращен из кэша ИЛИ он был фактически вычислен. Мы хотим добавить флаг в результирующую хэш-карту, который будет указывать на это. Однако все, что возвращается методом, кэшируется, поэтому не уверен, сможем ли мы сделать это в реализации метода calculate. Есть ли какой-нибудь способ узнать, был ли выполнен метод calculate ИЛИ возвращаемое значение поступает из кэша при вызове метода calculate?

Код, который мы используем для метода вычисления —

 @Cacheable(
        cacheNames = "request",
        key = "#cacheMapKey",           
        unless = "#result['ErrorMessage'] != null")
public Map<String, Object> calculate(Map<String, Object> cacheMapKey, Map<String, Object> message) {
    //method implementation
     return resu<
}
 

Комментарии:

1. Почему? Зачем вам знать, откуда оно берется?

2. Ну, вы могли бы добавить какой-нибудь перехватчик и т. Д. Но вам все равно придется помещать информацию в возвращаемый объект, иначе как бы вы ее получили? Кроме того, как уже говорил М. Дейнум, вам действительно нужно знать, что оно было вычислено или извлечено из кэша? Единственное, что я мог себе представить, что может иметь смысл здесь, это знать возраст данных — это можно решить, установив временную метку в объекте, которая указывает, когда выполнялось вычисление. Будет ли вновь вычисленный объект затем извлечен из кэша или нет, будет практически не важно.

3. Когда данные возвращаются из кэша, пользователи хотят знать, что они из кэша, а не в реальном времени.

4. Вы можете просто добавить метод, который добавит что-то на карту или полностью аннулирует кеш и изменит флаг на false. Затем, когда вы вызываете метод вычисления, он повторно кэширует результат и меняет этот флаг на true. Зарегистрируйте флаг для обоих методов, и так вы сможете убедиться, что кэш обновляется.

Ответ №1:

Проделав небольшую дополнительную работу, довольно просто добавить немного состояния в ваши @Cacheable методы обслуживания компонентов.

Я использую этот метод, когда отвечаю на подобные вопросы SO, чтобы показать, что значение получено из кэша по сравнению с метод обслуживания путем фактического вычисления значения. Например.

Вы заметите @Cacheable , @Service что class расширяет абстрактный базовый класс ( CacheableService ), чтобы помочь управлять «кэшируемым» состоянием. Таким образом, несколько @Cacheable @Service классов могут использовать эту функциональность, если это необходимо.

CacheableService Класс содержит методы для запроса состояния операции кэша, такие как isCacheMiss() и isCacheHit() . Внутри @Cacheable методов, при вызове из-за «промаха кэша», вы бы установили этот бит, вызвав setCacheMiss() . Опять же, setCacheMiss() метод вызывается так, внутри вашего @Cacheable метода обслуживания.

Однако, несколько слов предостережения!

Во-первых, хотя абстрактный CacheableService класс управляет состоянием cacheMiss бита с помощью потокобезопасного класса (т.Е. AtomicBoolean ), Сам CacheableService класс не является потокобезопасным при использовании в высококонкурентной среде, когда у вас есть несколько @Cacheable методов обслуживания, устанавливающих cacheMiss бит.

То есть, если у вас есть класс компонента с несколькими @Cacheable сервисными методами, все из которых устанавливают cacheMiss бит, используемый setCacheMiss() в многопоточной среде (что особенно true важно в веб-приложении), тогда можно прочитать устаревшее состояние cacheMiss при запросе бита. Это означает, cacheMiss что бит может быть true или false в зависимости от состояния кэша, вызываемой операции и чередования потоков. Поэтому в этом случае требуется больше работы, поэтому будьте осторожны, если вы полагаетесь на состояние cacheMiss бита для принятия важных решений.

Во-вторых, этот подход, использующий абстрактный CacheableService класс, не работает для репозиториев Spring Data (CRUD) на основе интерфейса. Как упоминали другие в комментариях, вы могли бы инкапсулировать эту логику кэширования в рекомендации AOP и перехватывать соответствующие вызовы, в данном случае. Лично я предпочитаю, чтобы управление кэшированием, безопасностью, транзакциями и т. Д. Осуществлялось на уровне обслуживания приложения, а не на уровне доступа к данным.

Наконец, несомненно, есть и другие ограничения, с которыми вы можете столкнуться, поскольку приведенный выше пример кода никогда не предназначался для производства, только для демонстрационных целей. Я оставляю это вам в качестве упражнения, чтобы выяснить, как сформировать эти биты для ваших нужд.