Начиная с версии 4.1, абстракция кэширования Spring обеспечивает полную поддержку аннотаций стандарта JCache (JSR-107): @CacheResult, @CachePut, @CacheRemove и @CacheRemoveAll, а также аннотации @CacheDefaults, @CacheKey и @CacheValue. Можно использовать эти аннотации даже без переноса вашего хранилища кэша на JSR-107. Внутренняя реализация использует абстракцию кэширования Spring и предусматривает реализации CacheResolver и KeyGenerator по умолчанию, которые соответствуют спецификации. Иными словами, если вы уже используете абстракцию кэширования Spring, то можете перейти на эти стандартизированные аннотации без изменения хранилища кэша (или конфигурации, если на то пошло).

Краткая характеристика

Для тех, кто знаком с аннотациями кэширования Spring, в следующей таблице описаны основные различия между аннотациями Spring и их аналогами из JSR-107:

Таблица 11. Аннотации кэширования из Spring в сравнении с аннотациями из JSR-107
Spring JSR-107 Комментарий

@Cacheable

@CacheResult

Довольно похожи. Аннотация @CacheResult может кэшировать определенные исключения и в принудительном порядке выполнять метод независимо от содержимого кэша.

@CachePut

@CachePut

В то время как Spring обновляет кэш результатом вызова метода, JCache требует, чтобы он был передан в качестве аргумента, аннотированного @CacheValue. Из-за этого различия JCache позволяет обновлять кэш перед или после фактического вызова метода.

@CacheEvict

@CacheRemove

Довольно похожи. Аннотация @CacheRemove поддерживает условное вытеснение, если вызов метода приводит к появлению исключения.

@CacheEvict(allEntries=true)

@CacheRemoveAll

См. аннотацию @CacheRemove.

@CacheConfig

@CacheDefaults

Позволяет конфигурировать те же понятия аналогичным образом.

В JCache есть понятие javax.cache.annotation.CacheResolver, которое идентично интерфейсу CacheResolver из Spring, за исключением того, что JCache поддерживает лишь один кэш. По умолчанию простая реализация извлекает используемый кэш на основе имени, объявленного в аннотации. Следует отметить, что если в аннотации не задано имя кэша, то автоматически генерируется имя по умолчанию. Для получения дополнительной информации см. javadoc по @CacheResult#cacheName().

Экземпляры CacheResolver извлекаются фабрикой CacheResolverFactory. Можно настроить эту фабрику для каждой операции кэширования, как показано в следующем примере:

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) (1)
public Book findBook(ISBN isbn)
  1. Настраиваем фабрику для этой операции.
Для всех ссылающихся классов Spring пытается найти бин с заданным типом. Если получено более одного совпадения, создается новый экземпляр, который может использовать обычные обратные вызовы жизненного цикла бина, такие как внедрение зависимостей.

Ключи генерируются при помощи javax.cache.annotation.CacheKeyGenerator, который служит той же цели, что и KeyGenerator для Spring. По умолчанию учитываются все аргументы метода, если хотя бы один параметр не аннотирован при помощи @CacheKey. Это похоже на объявление генерации кастомных ключей для Spring. Например, ниже приведены идентичные операции, одна из которых использует абстракцию Spring, а другая – JCache:

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@CacheResult(cacheName="books")
public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Также можно задать CacheKeyResolver в операции, аналогично тому, как задается CacheResolverFactory.

JCache может управлять исключениями, генерируемыми аннотированными методами. Таким образом, обновление кэша можно предотвратить, но также это можно использовать для кэширования исключения в качестве индикатора сбоя вместо повторного вызова метода. Предположим, что InvalidIsbnNotFoundException генерируется, если структура ISBN недопустима. Это необратимый сбой (ни одну книгу нельзя будет найти с таким параметром). Ниже приводится кэширование исключения, благодаря которому последующие вызовы с тем же самым, недопустимым ISBN генерируют кэшированное исключение напрямую вместо повторного вызова метода:

@CacheResult(cacheName="books", exceptionCacheName="failures"
            cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)

Активация средств поддержки JSR-107

Чтобы включить поддержку JSR-107 наряду с поддержкой декларативных аннотаций в Spring, не требуется делать что-то особенное. И аннотация @EnableCaching, и элемент cache:annotation-driven из XML автоматически активируют поддержку JCache, если в classpath присутствуют как API JSR-107, так и модуль spring-context-support.

В зависимости от сценария использования, выбор, по сути, за вами. Вы даже можете смешивать и сочетать службы, используя API JSR-107 для одних и собственные аннотации Spring – для других. Однако, если эти службы воздействуют на одни и те же кэши, следует использовать последовательную и идентичную реализацию генерации ключей.