Introduction à JCache

Introduction à JCache

1. Vue d'ensemble

En termes simples,JCache est l'API de mise en cache standard pour Java. Dans ce tutoriel, nous allons voir ce qu'est JCache et comment nous pouvons l'utiliser.

2. Dépendances Maven

Pour utiliser JCache, nous devons ajouter la dépendance suivante à nospom.xml:


    javax.cache
    cache-api
    1.0.0-PFD

Notez que nous pouvons trouver la dernière version de la bibliothèque dans lesMaven Central Repository.

Nous devons également ajouter une implémentation de l'API à nospom.xml; nous utiliserons Hazelcast ici:


    com.hazelcast
    hazelcast
    3.9-EA

On retrouve également la dernière version de Hazelcast à sesMaven Central Repository.

3. Implémentations JCache

JCache est implémenté par diverses solutions de mise en cache:

  • Implémentation de référence JCache

  • Hazelcast

  • Cohérence Oracle

  • Terre cuite Ehcache

  • Infinispan

Notez que, contrairement aux autres implémentations de référence,it’s not recommended to use JCache Reference Implementation in production since it causes some concurrency issues.

4. Composants principaux

4.1. Cache

L'interfaceCache dispose des méthodes utiles suivantes:

  • get() - prend la clé d'un élément comme paramètre et renvoie la valeur de l'élément; il renvoienull si la clé n'existe pas dans lesCache

  • getAll() - plusieurs clés peuvent être passées à cette méthode en tant que méthodeSet; telle renvoie les clés données et les valeurs associées sous forme deMap

  • getAndRemove() - la méthode récupère une valeur en utilisant sa clé et supprime l'élément desCache

  • put() - insère un nouvel élément dans leCache

  • clear() - supprime tous les éléments dans lesCache

  • containsKey() - vérifie si unCache contient une clé particulière

Comme on peut le constater, les noms des méthodes sont assez explicites. Pour plus d'informations sur ces méthodes et d'autres, visitez le siteJavadoc.

4.2. CacheManager

CacheManager est l'une des interfaces les plus importantes de l'API. Il nous permet d'établir, de configurer et de fermerCaches.

4.3. CachingProvider

CachingProvider est une interface qui nous permet de créer et de gérer le cycle de vie deCacheManagers.

4.4. Configuration

Configuration est une interface qui nous permet de configurerCaches. Il a une implémentation concrète -MutableConfiguration et une sous-interface -CompleteConfiguration.

5. Créer unCache

Voyons comment nous pouvons créer un simpleCache:

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

Tout ce que nous faisons est:

  • Création d'un objetCachingProvider, que nous utilisons pour construire un objetCacheManager

  • Création d'un objetMutableConfiguration, qui est une implémentation de l'interfaceConfiguration

  • Création d'un objetCache à l'aide de l'objetCacheManager que nous avons créé précédemment

  • En mettant toutes les entrées, nous devons mettre en cache dans notre objetCache

  • Fermer lesCacheManager pour libérer les ressources utilisées par lesCache

Si nous ne fournissons aucune implémentation de JCache dans nospom.xml, l'exception suivante sera levée:

javax.cache.CacheException: No CachingProviders have been configured

La raison en est que la machine virtuelle Java n'a trouvé aucune implémentation concrète de la méthodegetCacheManager().

6. EntryProcessor

EntryProcessor nous permet de modifier les entrées deCache en utilisant des opérations atomiques sans avoir à les rajouter auxCache. Pour l'utiliser, nous devons implémenter l'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;
    }
}

Maintenant, utilisons notre implémentationEntryProcessor:

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

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

7. Auditeurs de l'événement

Event Listeners allow us to take actions lors du déclenchement de l'un des types d'événements définis dans l'énumérationEventType, qui sont:

  • CRÉÉ

  • MIS À JOUR

  • SUPPRIMÉ

  • EXPIRÉ

Tout d'abord, nous devons implémenter les interfaces des événements que nous allons utiliser.

Par exemple, si nous voulons utiliser les types d'événementsCREATED etUPDATED, alors nous devons implémenter les interfacesCacheEntryCreatedListener etCacheEntryUpdatedListener.

Voyons un exemple:

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

Maintenant, exécutons notre test:

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

Dans un scénario réel, le cache peut lire les données du stockage réel.

Voyons un exemple. Tout d'abord, nous devons implémenter l'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;
    }
}

Et maintenant, utilisons notre implémentationCacheLoader:

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

Dans ce didacticiel, nous avons vu ce qu'est JCache et exploré certaines de ses fonctionnalités importantes dans quelques scénarios pratiques.

Comme toujours, l'implémentation complète de ce tutoriel peut être trouvéeover on GitHub.