Введение в JCache

Введение в JCache

1. обзор

Проще говоря,JCache - это стандартный API кеширования для Java. В этом руководстве мы увидим, что такое JCache и как его использовать.

2. Maven Зависимости

Чтобы использовать JCache, нам нужно добавить следующую зависимость к нашемуpom.xml:


    javax.cache
    cache-api
    1.0.0-PFD

Обратите внимание, что последнюю версию библиотеки можно найти в папкеMaven Central Repository.

Нам также нужно добавить реализацию API в нашpom.xml; здесь мы будем использовать Hazelcast:


    com.hazelcast
    hazelcast
    3.9-EA

Мы также можем найти последнюю версию Hazelcast по адресуMaven Central Repository.

3. Реализации JCache

JCache реализуется различными решениями для кэширования:

  • Реализация ссылок JCache

  • Hazelcast

  • Oracle Coherence

  • Терракотовая Ehcache

  • Infinispan

Обратите внимание, что, в отличие от других эталонных реализаций,it’s not recommended to use JCache Reference Implementation in production since it causes some concurrency issues.

4. Основные компоненты

4.1. Cacheс

ИнтерфейсCache имеет следующие полезные методы:

  • get() - принимает ключ элемента в качестве параметра и возвращает значение элемента; он возвращаетnull, если ключ не существует вCache

  • getAll() - этому методу можно передать несколько ключей, поскольку методSet; the возвращает заданные ключи и связанные значения какMap

  • getAndRemove() - метод извлекает значение, используя свой ключ, и удаляет элемент изCache

  • put() - вставляет новый элемент вCache

  • clear() - удаляет все элементы вCache

  • containsKey() - проверяет, содержит лиCache определенный ключ

Как мы видим, имена методов в значительной степени говорят сами за себя. Для получения дополнительной информации об этих и других методах посетитеJavadoc.

4.2. CacheManagerс

CacheManager - один из самых важных интерфейсов API. Это позволяет нам устанавливать, настраивать и закрыватьCaches.

4.3. CachingProviderс

CachingProvider - это интерфейс, который позволяет нам создавать и управлять жизненным цикломCacheManagers.

4.4. Configurationс

Configuration - это интерфейс, который позволяет нам настраиватьCaches. У него есть одна конкретная реализация -MutableConfiguration и подинтерфейс -CompleteConfiguration.

5. СозданиеCache

Давайте посмотрим, как мы можем создать простойCache:

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();
MutableConfiguration config
  = new MutableConfiguration<>();
Cache cache = cacheManager
  .createCache("simpleCache", config);
cache.put("key1", "value1");
cache.put("key2", "value2");
cacheManager.close();

Все, что мы делаем, это:

  • Создание объектаCachingProvider, который мы используем для создания объектаCacheManager

  • Создание объектаMutableConfiguration, который является реализацией интерфейсаConfiguration

  • Создание объектаCache с использованием объектаCacheManager, который мы создали ранее

  • Помещая все записи, нам нужно кешировать в наш объектCache

  • ЗакрытиеCacheManager для высвобождения ресурсов, используемыхCache

Если мы не предоставим какую-либо реализацию JCache в нашемpom.xml, будет выдано следующее исключение:

javax.cache.CacheException: No CachingProviders have been configured

Причина в том, что JVM не смогла найти конкретную реализацию методаgetCacheManager().

6. EntryProcessorс

EntryProcessor позволяет нам изменять записиCache, используя атомарные операции, без необходимости повторно добавлять их вCache. Чтобы использовать его, нам нужно реализовать интерфейсEntryProcessor:

public class SimpleEntryProcessor
  implements EntryProcessor, Serializable {

    public String process(MutableEntry 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. Слушатели событий

Event Listeners allow us to take actions при запуске любого из типов событий, определенных в перечисленииEventType, а именно:

  • СОЗДАНО

  • ОБНОВЛЕНО

  • СНЯТО

  • ИСТЕКШИЙ

Во-первых, нам нужно реализовать интерфейсы событий, которые мы собираемся использовать.

Например, если мы хотим использовать типы событийCREATED иUPDATED, мы должны реализовать интерфейсыCacheEntryCreatedListener иCacheEntryUpdatedListener.

Давайте посмотрим на пример:

public class SimpleCacheEntryListener implements
  CacheEntryCreatedListener,
  CacheEntryUpdatedListener,
  Serializable {

    private boolean updated;
    private boolean created;

    // standard getters

    public void onUpdated(
      Iterable> events) throws CacheEntryListenerException {
        this.updated = true;
    }

    public void onCreated(
      Iterable> events) throws CacheEntryListenerException {
        this.created = true;
    }
}

Теперь давайте запустим наш тест:

@Test
public void whenRunEvent_thenCorrect() throws InterruptedException {
    this.listenerConfiguration
      = new MutableCacheEntryListenerConfiguration(
        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 allowsus to use read-through modeto treat cache as the main data store and read data from it.

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

Давайте посмотрим на пример. Во-первых, мы должны реализовать интерфейсCacheLoader:

public class SimpleCacheLoader
  implements CacheLoader {

    public String load(Integer key) throws CacheLoaderException {
        return "fromCache" + key;
    }

    public Map loadAll(Iterable keys)
      throws CacheLoaderException {
        Map data = new HashMap<>();
        for (int key : keys) {
            data.put(key, load(key));
        }
        return data;
    }
}

А теперь давайте воспользуемся нашей реализациейCacheLoader:

public class CacheLoaderTest {

    private Cache cache;

    @Before
    public void setup() {
        CachingProvider cachingProvider = Caching.getCachingProvider();
        CacheManager cacheManager = cachingProvider.getCacheManager();
        MutableConfiguration 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, и изучили некоторые из его важных функций в нескольких практических сценариях.

Как всегда, полную реализацию этого руководства можно найти вover on GitHub.