Статьи

Новый стандарт кэширования Java (javax.cache)

В этом посте рассматривается новый стандарт кэширования Java: javax.cache.

Как это вписывается в экосистему Java

Этот стандарт разрабатывается JSR107 , автором которого является ведущий специалист. JSR107 включен в Java EE 7, разрабатываемую JSR342 . Java EE 7 должна быть завершена в конце 2012 года. Но пока javax.cache будет работать в средах Java SE 6 и выше, а также в Java EE 6, а также в Spring и других популярных средах.

JSR107 имеет черновой статус. В настоящее время мы находимся в выпуске 0.3 API, эталонной реализации и TCK. Примеры кода в этой статье работают против этой версии.

Принятие

Продавцы, которые являются активными членами экспертной группы или проявили интерес к внедрению спецификации:

  • Терракотовая — Ehcache
  • Oracle — согласованность
  • JBoss — Infinispan
  • IBM — ExtemeScale
  • SpringSource — Gemfire
  • GridGain
  • TMax
  • Google App Engine Java

Terracotta выпустит модуль для Ehcache, чтобы он совпал с окончательным проектом, а затем обновит его, если это потребуется для окончательной версии.

Особенности

С точки зрения дизайна, основными понятиями являются CacheManager, который содержит и контролирует коллекцию Cache. Кэши имеют записи. Базовый API-интерфейс можно представить в виде карты со следующими дополнительными функциями:

  • атомарные операции, похожие на java.util.ConcurrentMap
  • сквозное кэширование
  • сквозное кэширование
  • кеш слушателей
  • статистика
  • транзакции, включая все уровни изоляции
  • кэширование аннотаций
  • общие кэши, которые содержат определенный ключ и тип значения
  • определение хранилища по ссылке (применимо только для кэшей кучи) и хранилище по значению

Дополнительные функции

Вместо того, чтобы разбивать спецификацию на несколько изданий, ориентированных на разные группы пользователей, таких как Java SE и Spring / EE, мы выбрали другой подход.

Во-первых, для кэширования в стиле Java SE нет никаких зависимостей. А для Spring / EE, где вы, возможно, захотите использовать аннотации и / или транзакции, эти структуры будут удовлетворять зависимости.

Во-вторых, у нас есть API возможностей через ServiceProvider.isSupported (функция OptionalFeature), чтобы вы могли определить во время выполнения, каковы возможности реализации. Дополнительные функции:

  • storeByReference — по умолчанию используется storeByValue
  • транзакционный
  • аннотации

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

Хорошо для автономного и распределенного кэширования

Хотя в спецификации не предусмотрена конкретная топология распределенного кэша, очевидно, что кэши вполне могут быть распределены. У нас есть один API, который охватывает оба случая, но он чувствителен к распределенным проблемам. Например, CacheEntryListener имеет NotificationScope событий, которые он прослушивает, так что события могут быть ограничены локальной доставкой. У нас нет методов, подобных карте высокой стоимости, таких как keySet () и values ​​(). И мы обычно предпочитаем нулевой или недорогой тип возврата. Таким образом, в то время как Map имеет V put (ключ K, значение V), javax.cache.Cache имеет void put (ключ K, значение V)
загрузки классов

Кэши содержат данные, совместно используемые несколькими потоками, которые сами могут выполняться в разных приложениях-контейнерах или пакетах OSGi в одной JVM и могут быть распределены по нескольким JVM в кластере. Это усложняет загрузку классов.

Мы решили эту проблему. При создании CacheManager может быть указан загрузчик классов. Если ничего не указано, реализация предоставляет значение по умолчанию. В любом случае десериализация объекта будет использовать загрузчик классов CacheManager.

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

Получение кода

Спецификация находится в Maven Central. Фрагмент Maven это:

1
2
3
4
5
<dependency>
     <groupId>javax.cache</groupId>
     <artifactId>cache-api</artifactId>
     <version>0.3</version>
</dependency>

Кулинарный тур по API

Создание CacheManager

Мы поддерживаем творческий подход Java 6 java.util.ServiceLoader. Он автоматически обнаружит реализацию кэша в вашем пути к классам. Затем вы создаете CacheManager с:

1
CacheManager cacheManager = Caching.getCacheManager();

который возвращает одноэлементный CacheManager с именем «__default__». Последующие вызовы возвращают тот же CacheManager.

В CacheManager могут быть настроены имена и загрузчики классов. Например,

1
2
CacheManager cacheManager =
 Caching.getCacheManager("app1", Thread.currentThread().getContextClassLoader());

Реализации могут также поддерживать прямое создание с новым для максимальной гибкости:

1
2
CacheManager cacheManager =
 new RICacheManager("app1", Thread.currentThread().getContextClassLoader());

Или сделать то же самое без добавления зависимости времени компиляции от какой-либо конкретной реализации:

1
2
3
4
5
String className = "javax.cache.implementation.RIServiceProvider";
Class<ServiceProvider> clazz =
 (Class<ServiceProvider>)Class.forName(className);
ServiceProvider provider = clazz.newInstance();
return provider.createCacheManager(Thread.currentThread().getContextClassLoader(), "app1");

Мы ожидаем, что реализации будут иметь свои хорошо известные файлы конфигурации, которые будут использоваться для настройки CacheManager. Имя CacheManager может использоваться для различения файла конфигурации. Для ehcache это будет знакомый файл ehcache.xml, помещенный в корень пути к классам с префиксом дефиса для имени CacheManager. Таким образом, по умолчанию CacheManager будет просто ehcache.xml, а «myCacheManager» будет app1-ehcache.xml.

Создание кеша

API поддерживает программное создание кешей. Это дополняет обычное соглашение о декларативной настройке кешей, которое предоставляется каждому поставщику.

Программно настроить кэш с именем «testCache», который установлен для чтения

1
2
3
4
5
cacheManager = getCacheManager();
CacheConfiguration cacheConfiguration = cacheManager.createCacheConfiguration();
cacheConfiguration.setReadThrough(true);
Cache testCache = cacheManager.createCacheBuilder("testCache")
 .setCacheConfiguration(cacheConfiguration).build();

Получение ссылки на кеш

Вы получаете кеши от CacheManager. Чтобы получить кеш под названием «testCache»:

1
Cache<Integer, Date> cache = cacheManager.getCache("testCache");

Основные операции с кэшем

Положить в кеш:

1
2
3
4
Cache<Integer, Date> cache = cacheManager.getCache(cacheName);
Date value1 = new Date();
Integer key = 1;
cache.put(key, value1);

Чтобы достать из кеша:

1
2
3
Cache<Integer, Date> cache =
 cacheManager.getCache(cacheName);
Date value2 = cache.get(key);

Чтобы удалить из кэша:

1
2
3
4
Cache<Integer, Date> cache =
 cacheManager.getCache(cacheName);
Integer key = 1;
cache.remove(key);

Аннотации

JSR107 представляет стандартизированный набор аннотаций кэширования, которые осуществляют перехват кэширования на уровне методов для аннотированных классов, работающих в контейнерах внедрения зависимостей. Аннотации кэширования становятся все более популярными, начиная с аннотаций Ehcache для Spring , которые затем повлияли на аннотации кэширования в Spring 3.

Аннотации JSR107 охватывают наиболее распространенные операции кэширования, включая:

  • @CacheResult — использовать кеш
  • @CachePut — положить в кеш
  • @CacheRemoveEntry — удалить одну запись из кэша
  • @CacheRemoveAll — удалить все записи из кэша

Когда требуемое имя кэша, ключ и значение могут быть введены, они не требуются. См. JavaDoc для деталей. Чтобы обеспечить больший контроль, вы можете указать все это и многое другое. В следующем примере атрибут cacheName указан как «domainCache», индекс указан как ключ, а домен — как значение.

1
2
3
4
5
6
7
public class DomainDao {
     @CachePut(cacheName="domainCache")
     public void updateDomain(String domainId, @CacheKeyParam int index,
 @CacheValue Domain domain) {
 ...
     }
}

Эталонная реализация включает в себя реализацию как для Spring, так и для CDI. CDI — это стандартизированная инъекция, управляемая контейнером, представленная в Java EE 6. Реализация хорошо модульна для повторного использования, использует лицензию Apache, и поэтому мы ожидаем, что несколько кешей с открытым исходным кодом будут использовать их повторно. Хотя мы еще не сделали реализацию для Guice, это легко сделать.

Пример аннотации

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class BlogManager {
 
 @CacheResult(cacheName="blogManager")
 public Blog getBlogEntry(String title) {...}
 
 @CacheRemoveEntry(cacheName="blogManager")
 public void removeBlogEntry(String title) {...}
 
 @CacheRemoveAll(cacheName="blogManager")
 public void removeAllBlogs() {...}
 
 @CachePut(cacheName="blogManager")
 public void createEntry(@CacheKeyParam String title, @CacheValue Blog blog) {...}
 
 @CacheResult(cacheName="blogManager")
 public Blog getEntryCached(String randomArg, @CacheKeyParam String title){...}
 
}

Проводка до весны

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

1
<jcache-spring:annotation-driven proxy-target-class="true"/>

Полный пример:

1
2
3
4
5
<beans>
 <context:annotation-config/>
 <jcache-spring:annotation-driven proxy-target-class="true"/>
 <bean id="cacheManager" factory-method="getCacheManager" />
</beans>

Spring имеет свои собственные аннотации кеширования, основанные на более ранней работе от автора JSR107 Эрика Дальквиста. Эти аннотации и JSR107 будут счастливо сосуществовать.

Подключение CDI

Сначала создайте реализацию javax.cache.annotation.BeanProvider, а затем скажите CDI, где найти его, объявив ресурс с именем javax.cache.annotation.BeanProvider в пути к классам в / META-INF / services /.

Для примера использования реализации Weld для CDI см. CdiBeanProvider в нашем тестовом комплекте CDI.

Дальнейшее чтение

Для дальнейшего чтения посетите домашнюю страницу JSR по адресу https://github.com/jsr107/jsr107spec .

Ссылка: javax.cache: новый стандарт кэширования Java от нашего партнера по JCG Грега Лака в блоге Грега Лака .

Статьи по Теме :