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 и перехватывать соответствующие вызовы, в данном случае. Лично я предпочитаю, чтобы управление кэшированием, безопасностью, транзакциями и т. Д. Осуществлялось на уровне обслуживания приложения, а не на уровне доступа к данным.
Наконец, несомненно, есть и другие ограничения, с которыми вы можете столкнуться, поскольку приведенный выше пример кода никогда не предназначался для производства, только для демонстрационных целей. Я оставляю это вам в качестве упражнения, чтобы выяснить, как сформировать эти биты для ваших нужд.