1. Обзор
Проще говоря, JCache — это стандартный API кэширования для Java. В этом уроке мы увидим, что такое JCache и как мы можем его использовать.
2. Зависимости Maven
Чтобы использовать JCache, нам нужно добавить следующую зависимость в наш pom.xml
:
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.1</version>
</dependency>
Обратите внимание, что последнюю версию библиотеки мы можем найти в центральном репозитории Maven .
Нам также нужно добавить реализацию API в наш pom.xml
; мы будем использовать Hazelcast здесь:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>5.1.1</version>
</dependency>
Мы также можем найти последнюю версию Hazelcast в центральном репозитории Maven .
3. Реализации JCache
JCache реализуется различными решениями для кэширования:
- Эталонная реализация JCache
- Хейзелкаст
- Согласованность оракула
- Терракотовый тайник
- Инфиниспан
Обратите внимание, что, в отличие от других эталонных реализаций, не рекомендуется использовать эталонную реализацию JCache в рабочей среде, так как это вызывает некоторые проблемы параллелизма.
4. Основные компоненты
4.1. Кэш
Интерфейс Cache
имеет следующие полезные методы:
get()
– принимает в качестве параметра ключ элемента и возвращает значение элемента; возвращаетноль
, если ключ не существует вкеше
getAll()
— этому методу можно передать несколько ключей в видеSet; метод
возвращает заданные ключи и связанные значения в видекарты
getAndRemove()
— метод извлекает значение по своему ключу и удаляет элемент изкэша .
put()
— вставляет новый элемент вкэш
clear()
— удаляет все элементы вкеше
containsKey ()
— проверяет, содержит ликэш
определенный ключ
Как мы видим, названия методов говорят сами за себя. Для получения дополнительной информации об этих и других методах посетите Javadoc .
4.2. CacheManager
CacheManager
— один из самых важных интерфейсов API. Это позволяет нам устанавливать, настраивать и закрывать кэши
.
4.3. CachingProvider
CachingProvider
— это интерфейс, который позволяет нам создавать и управлять жизненным циклом CacheManagers
.
4.4. Конфигурация
Configuration
— это интерфейс, который позволяет нам настраивать Caches
. У него есть одна конкретная реализация — MutableConfiguration
и субинтерфейс — CompleteConfiguration
.
5. Создание кеша
Давайте посмотрим, как мы можем создать простой Cache
:
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();
MutableConfiguration<String, String> config
= new MutableConfiguration<>();
Cache<String, String> cache = cacheManager
.createCache("simpleCache", config);
cache.put("key1", "value1");
cache.put("key2", "value2");
cacheManager.close();
Все, что мы делаем, это:
- Создание объекта
CachingProvider
, который мы используем для создания объектаCacheManager .
- Создание объекта
MutableConfiguration
, который является реализацией интерфейсаConfiguration .
- Создание объекта
Cache
с использованием объектаCacheManager
, который мы создали ранее - Помещая все записи, нам нужно кэшировать в наш объект
Cache
- Закрытие
CacheManager
для освобождения ресурсов, используемыхкешем
Если мы не обеспечим какую-либо реализацию JCache в нашем pom.xml
, будет выдано следующее исключение:
javax.cache.CacheException: No CachingProviders have been configured
Причина этого в том, что JVM не смогла найти какой-либо конкретной реализации метода getCacheManager()
.
6. Входной процессор
EntryProcessor
позволяет нам изменять записи Cache
с помощью атомарных операций без необходимости повторного добавления их в Cache
. Чтобы использовать его, нам нужно реализовать интерфейс EntryProcessor
:
public class SimpleEntryProcessor
implements EntryProcessor<String, String, String>, Serializable {
public String process(MutableEntry<String, String> entry, Object... args)
throws EntryProcessorException {
if (entry.exists()) {
String current = entry.getValue();
entry.setValue(current + " - modified");
return current;
}
return null;
}
}
Теперь воспользуемся нашей реализацией EntryProcessor
:
@Test
public void whenModifyValue_thenCorrect() {
this.cache.invoke("key", new SimpleEntryProcessor());
assertEquals("value - modified", cache.get("key"));
}
7. Слушатели событий
Прослушиватели событий позволяют нам выполнять действия при запуске любого из типов событий, определенных в перечислении EventType
, а именно:
СОЗДАННЫЙ
ОБНОВЛЕНО
УДАЛЕННЫЙ
ИСТЕКШИЙ
Во-первых, нам нужно реализовать интерфейсы событий, которые мы собираемся использовать.
Например, если мы хотим использовать типы событий CREATED
и UPDATED
, то мы должны реализовать интерфейсы CacheEntryCreatedListener
и CacheEntryUpdatedListener
.
Давайте посмотрим пример:
public class SimpleCacheEntryListener implements
CacheEntryCreatedListener<String, String>,
CacheEntryUpdatedListener<String, String>,
Serializable {
private boolean updated;
private boolean created;
// standard getters
public void onUpdated(
Iterable<CacheEntryEvent<? extends String,
? extends String>> events) throws CacheEntryListenerException {
this.updated = true;
}
public void onCreated(
Iterable<CacheEntryEvent<? extends String,
? extends String>> events) throws CacheEntryListenerException {
this.created = true;
}
}
Теперь давайте запустим наш тест:
@Test
public void whenRunEvent_thenCorrect() throws InterruptedException {
this.listenerConfiguration
= new MutableCacheEntryListenerConfiguration<String, String>(
FactoryBuilder.factoryOf(this.listener), null, false, true);
this.cache.registerCacheEntryListener(this.listenerConfiguration);
assertEquals(false, this.listener.getCreated());
this.cache.put("key", "value");
assertEquals(true, this.listener.getCreated());
assertEquals(false, this.listener.getUpdated());
this.cache.put("key", "newValue");
assertEquals(true, this.listener.getUpdated());
}
8. Кэш-загрузчик
CacheLoader
позволяет нам использовать режим сквозного чтения, чтобы рассматривать кеш как основное хранилище данных и считывать данные из него .
В реальном сценарии мы можем заставить кеш считывать данные из реального хранилища.
Давайте посмотрим на пример. Во-первых, мы должны реализовать интерфейс CacheLoader
:
public class SimpleCacheLoader
implements CacheLoader<Integer, String> {
public String load(Integer key) throws CacheLoaderException {
return "fromCache" + key;
}
public Map<Integer, String> loadAll(Iterable<? extends Integer> keys)
throws CacheLoaderException {
Map<Integer, String> data = new HashMap<>();
for (int key : keys) {
data.put(key, load(key));
}
return data;
}
}
А теперь воспользуемся нашей реализацией CacheLoader
:
public class CacheLoaderTest {
private Cache<Integer, String> cache;
@Before
public void setup() {
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();
MutableConfiguration<Integer, String> config
= new MutableConfiguration<>()
.setReadThrough(true)
.setCacheLoaderFactory(new FactoryBuilder.SingletonFactory<>(
new SimpleCacheLoader()));
this.cache = cacheManager.createCache("SimpleCache", config);
}
@Test
public void whenReadingFromStorage_thenCorrect() {
for (int i = 1; i < 4; i++) {
String value = cache.get(i);
assertEquals("fromCache" + i, value);
}
}
}
9. Заключение
В этом руководстве мы увидели, что такое JCache, и изучили некоторые его важные функции в нескольких практических сценариях.
Как всегда, полную реализацию этого туториала можно найти на GitHub .