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 extends Integer> 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.