Introdução ao JCache

Introdução ao JCache

1. Visão geral

Simplificando,JCache é a API de cache padrão para Java. Neste tutorial, veremos o que é JCache e como podemos usá-lo.

2. Dependências do Maven

Para usar o JCache, precisamos adicionar a seguinte dependência ao nossopom.xml:


    javax.cache
    cache-api
    1.0.0-PFD

Observe que podemos encontrar a versão mais recente da biblioteca emMaven Central Repository.

Também precisamos adicionar uma implementação da API ao nossopom.xml; vamos usar Hazelcast aqui:


    com.hazelcast
    hazelcast
    3.9-EA

Também podemos encontrar a versão mais recente do Hazelcast em seuMaven Central Repository.

3. Implementações JCache

O JCache é implementado por várias soluções de cache:

  • Implementação de referência do JCache

  • Hazelcast

  • Coerência do Oracle

  • Terracota Ehcache

  • Infinispan

Observe que, ao contrário de outras implementações de referência,it’s not recommended to use JCache Reference Implementation in production since it causes some concurrency issues.

4. Componentes principais

4.1. Cache

A interfaceCache tem os seguintes métodos úteis:

  • get() - pega a chave de um elemento como parâmetro e retorna o valor do elemento; ele retornanull se a chave não existe noCache

  • getAll() - várias chaves podem ser passadas para este método comoSet; t; o método retorna as chaves fornecidas e os valores associados comoMap

  • getAndRemove() - o método recupera um valor usando sua chave e remove o elemento deCache

  • put() - insere um novo item noCache

  • clear() - remove todos os elementos emCache

  • containsKey() - verifica se umCache contém uma chave particular

Como podemos ver, os nomes dos métodos são praticamente auto-explicativos. Para obter mais informações sobre esses métodos e outros, visiteJavadoc.

4.2. CacheManager

CacheManager é uma das interfaces mais importantes da API. Ele nos permite estabelecer, configurar e fecharCaches.

4.3. CachingProvider

CachingProvider é uma interface que nos permite criar e gerenciar o ciclo de vida deCacheManagers.

4.4. Configuration

Configuration é uma interface que nos permite configurarCaches. Tem uma implementação concreta -MutableConfiguratione uma subinterface -CompleteConfiguration.

5. Criando umCache

Vamos ver como podemos criar umCache simples:

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();

Tudo o que estamos fazendo é:

  • Criando um objetoCachingProvider, que estamos usando para construir um objetoCacheManager

  • Criação de um objetoMutableConfiguration, que é uma implementação da interfaceConfiguration

  • Criando um objetoCache usando o objetoCacheManager que criamos anteriormente

  • Colocando todas as entradas, precisamos armazenar em cache em nosso objetoCache

  • Fechando oCacheManager para liberar os recursos usados ​​peloCache

Se não fornecermos nenhuma implementação de JCache em nossopom.xml, a seguinte exceção será lançada:

javax.cache.CacheException: No CachingProviders have been configured

A razão para isso é que a JVM não conseguiu encontrar nenhuma implementação concreta do métodogetCacheManager().

6. EntryProcessor

EntryProcessor nos permite modificar entradas deCache usando operações atômicas sem ter que adicioná-las novamente aoCache. Para usá-lo, precisamos implementar a interfaceEntryProcessor:

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;
    }
}

Agora, vamos usar nossa implementaçãoEntryProcessor:

@Test
public void whenModifyValue_thenCorrect() {
    this.cache.invoke("key", new SimpleEntryProcessor());

    assertEquals("value - modified", cache.get("key"));
}

7. Ouvintes de Eventos

Event Listeners allow us to take actions ao disparar qualquer um dos tipos de eventos definidos na enumEventType, que são:

  • CRIADA

  • ATUALIZADA

  • REMOVIDO

  • EXPIRADO

Primeiro, precisamos implementar interfaces dos eventos que vamos usar.

Por exemplo, se quisermos usar os tipos de eventoCREATEDeUPDATED, devemos implementar as interfacesCacheEntryCreatedListenereCacheEntryUpdatedListener.

Vamos ver um exemplo:

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;
    }
}

Agora, vamos fazer nosso teste:

@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.

Em um cenário do mundo real, podemos fazer com que o cache leia os dados do armazenamento real.

Vamos dar uma olhada em um exemplo. Primeiro, devemos implementar a interfaceCacheLoader:

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;
    }
}

E agora, vamos usar nossa implementaçãoCacheLoader:

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. Conclusão

Neste tutorial, vimos o que é JCache e exploramos alguns de seus recursos importantes em alguns cenários práticos.

Como sempre, a implementação completa deste tutorial pode ser encontradaover on GitHub.