Mise en cache au printemps avec les annotations EhCache

Publié: 2022-03-11

EhCache est un cache Java pur largement utilisé qui peut être facilement intégré aux frameworks Java les plus populaires, tels que Spring et Hibernate. Il est souvent considéré comme le choix le plus pratique pour les applications Java car il peut être facilement intégré dans des projets. En particulier:

  • Il peut être configuré en incluant simplement le JAR dans votre projet. Aucune étape d'installation supplémentaire n'est requise.
  • Il s'exécute dans le même processus avec l'application, donc c'est rapide. Aucun service supplémentaire n'est requis pour fonctionner.

En bref, EhCache est un excellent choix pour toute application purement Java.

De plus, EhCache Spring Annotations permet une intégration transparente dans n'importe quelle application Spring en ajoutant simplement des annotations aux méthodes pouvant être mises en cache, sans modifier les implémentations des méthodes.

EhCache est une excellente solution de mise en cache pour les projets Spring.

Bien qu'EhCache fournisse des API simples et riches pour manipuler le cache par programme, cet article se concentre principalement sur l'amélioration de vos applications Spring de manière moins intrusive avec EhCache Spring Annotations. Nous allons configurer un projet Spring MVC et déployer un service Web RESTful dans Tomcat. Ensuite, EhCache sera intégré au service web.

Aperçu du projet

Nous allons démontrer EhCache Annotations dans le cadre d'un exemple de projet. Nous allons configurer un service Web basé sur Spring MVC hébergé sur un serveur Tomcat 8.

J'ai développé le projet dans Eclipse, qui peut être installé en suivant les instructions ici.

La dernière version stable de Tomcat, Tomcat 8, peut être téléchargée ici.

Bien entendu, ces plateformes spécifiques ne sont pas obligatoires pour EhCache ; vous pouvez toujours choisir votre IDE et votre serveur préférés.

Le fichier JAR EhCache Spring Annotations est disponible ici. Comme nous pouvons le voir, il existe deux fichiers JAR pour chaque version : un avec des dépendances et un sans. Celui avec des dépendances comprend également EhCache 2 et Spring 3, qui sont nécessaires pour que les annotations EhCache fonctionnent. Il est plus facile à configurer si nous téléchargeons celui avec des dépendances et l'ajoutons à notre chemin de construction.

EhCache Spring Annotations est également compatible avec Spring 4, bien qu'il doive être configuré séparément. Il n'est pas clair si le projet prendra en charge EhCache 3 dans un proche avenir. Pour ceux qui utilisent ou ont l'intention d'utiliser EhCache 3, l'approche d'annotation décrite dans cet article n'est pas conseillée.

Enfin, nous utiliserons Maven pour tout gérer. Maven est livré préemballé avec la plupart des installations Eclipse, mais il peut également être obtenu ici. Les dépendances Spring MVC et EhCache Spring Annotations peuvent être ajoutées assez facilement, comme indiqué plus loin dans cet article.

Configuration du projet

Si vous n'avez jamais mis en place de projet Spring auparavant, vous trouverez peut-être également informatif le post de Stefan Varga sur le sujet.

Pour cette démonstration, nous allons mettre en place un projet de base en utilisant l'archétype Maven maven-archetype-webapp . La structure globale du fichier ressemblera à ceci :

Structure initiale du projet Spring.

Créez un répertoire, src/main/java , avec trois packages : com.toptal.blog , com.toptal.blog.cache et com.toptal.blog.service . Notre source d'application ira dans ces packages, comme décrit plus bas.

Définissons un servlet Tomcat appelé "springrest" dans web.xml :

 <web-app> ... <servlet> <servlet-name>springrest</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springrest</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>

Sauf indication contraire explicite, un Spring MVC DispatcherServlet recherchera un fichier de configuration XML nommé {servlet-name}-servlet.xml dans le répertoire WEB-INF . Créons un fichier de configuration appelé springrest-servlet.xml . Pour activer les méthodes de contrôleur de processus Spring annotées avec @RequestMapping , ajoutons simplement <mvc:annotation-driven /> à ce fichier. Définissons également le package de base pour que Spring analyse et enregistre automatiquement les beans en ajoutant <context:component-scan base-package="com.toptal.blog" /> . La configuration springrest-servlet.xml devient :

 <beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>

Un service Web RESTful simple

Maintenant que notre projet est correctement configuré, implémentons une simple API de "service de messagerie". Dans notre package de base, project.toptal.blog , nous ajouterons SpringRestControllerWithEhCache.java , avec une méthode GET pour obtenir un message par ID et une méthode POST pour définir un message par ID :

 @RestController @RequestMapping( "/" ) public class SpringRestControllerWithEhCache { @Autowired MessageService messageService; @RequestMapping( value = "/message/{id}", method = RequestMethod.GET ) public String getMessage( @PathVariable Integer id ) { String message = messageService.getMessage( id ); System.out.println( "get message ["+message+"] at "+new Date() ); return message; } @RequestMapping( value = "/message/set/{id}/{message}", method = RequestMethod.POST ) public String setMessage( @PathVariable Integer id, @PathVariable String message ) { System.out.println( "set message ["+message+"] at "+new Date() ); messageService.setMessage( id, message ); return message; } }

Nous définirons la classe MessageService dans com.toptal.blog.service . Il accédera aux messages stockés dans notre système d'enregistrements (SOR). Dans une application de production, le SOR ressemblerait à une base de données relationnelle. Pour simplifier, nous utiliserons un HashMap :

 @Service public class MessageService { private ConcurrentHashMap<Integer, String> messages = new ConcurrentHashMap<Integer, String>(); public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } public void setMessage( Integer id, String message ){ messages.put( id, message ); } }

Maintenant, si nous exportons le projet en tant que WAR et le déployons dans Tomcat, nous devrions pouvoir définir un message, par exemple "test_message", pour ID=1, en créant une requête HTTP POST à http://localhost:8080/EhCacheExample/message/set/1/test_message . Nous devrions alors pouvoir récupérer "test_message" avec une requête HTTP GET à http://localhost:8080/EhCacheExample/message/1 . J'ai utilisé Insomnia comme client REST pratique pour faire mon test.

Branchez les annotations de printemps EhCache

Maintenant, laissons EhCache travailler pour nous. Il suffit de quelques étapes rapides pour configurer notre projet afin qu'il exécute correctement EhCache.

EhCache Spring Annotations rend EhCache facile et transparent à déployer dans votre application.

Étape 1 : Mettre à jour les dépendances pour utiliser les annotations EhCache Spring

Ajoutez la dépendance EhCache Spring Annotations dans le pom.xml de Maven :

 <!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>

Étape 2 : Configurer un gestionnaire de cache personnalisé

Spring a un gestionnaire de cache EhCache intégré, org.springframework.cache.ehcache.EhCacheManagerFactoryBean . Cela convient à la plupart des situations de mise en cache, mais j'ai trouvé utile de définir un gestionnaire de cache personnalisé car cela me permet de contrôler le cache soit par programme, soit avec des annotations, en utilisant le même gestionnaire de cache. Cet article se concentre sur les annotations, mais allons-y et définissons un gestionnaire de cache personnalisé afin que nous soyons prêts au cas où nous en aurions besoin. Si vous préférez vous en tenir au gestionnaire de cache par défaut, vous pouvez ignorer cette étape.

Nous allons définir la nouvelle classe dans com.toptal.blog.cache.CustomCacheManager :

 public class CustomCacheManager extends net.sf.ehcache.CacheManager{ public CustomCacheManager(){ super(); } /* Add your own cache methods here. * * public void myCustomCacheMethod(){ * // your code here * } * */ }

Activez-le en mettant à jour springrest-servlet.xml comme suit :

 ... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...

Étape 3 : Configurer EhCache

Enfin, créez le fichier de configuration ehcache.xml dans le classpath. Par défaut, Eclipse inclura src/main/resources dans le classpath, et nous placerons le fichier ici. Ce fichier est nécessaire au bon fonctionnement d'EhCache. Il définit les noms de cache et certaines propriétés de chaque cache, telles que timeToLiveSeconds :

 <ehcache xmlms:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <diskStore path="cache" /> <cache name="messageCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="10" overflowToDisk="false" memoryStoreEvictionPolicy="LFU" /> </ehcache>

Étape 4 : Tester le cache

Maintenant, avec tout configuré et prêt à l'emploi, l'utilisation d'EhCache devrait être un travail facile et agréable. Nous pouvons simplement ajouter @Cacheable à la méthode ou à la classe que nous voulons mettre en cache. Par exemple, j'ai ajouté @Cacheable à la méthode getMessage dans MessageService . C'est si facile!

 @Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); }

Pour tester que notre cache fonctionne, nous pouvons créer un message pour ID=1 en émettant une requête HTTP POST à http://localhost:8080/EhCacheExample/message/set/1/newMessage , puis obtenir le message pour ID= 1 plusieurs fois, avec des requêtes GET à http://localhost:8080/EhCacheExample/message/1 . Comme vous pouvez le voir dans la sortie de la console ci-dessous, le service Web demande au SOR d'obtenir le message la première fois que nous demandons le message, mais pas pour les deux requêtes suivantes, renvoyant le message mis en cache à la place. Comme nous avons défini timeToLiveSeconds sur 10, le service Web appelle le SOR pour récupérer le message après 10 secondes :

 set message [newMessage] at Sun Dec 06 23:55:39 MST 2015 get message [newMessage] at Sun Dec 06 23:55:42 MST 2015 Getting data from SOR...... get message [newMessage] at Sun Dec 06 23:55:47 MST 2015 get message [newMessage] at Sun Dec 06 23:55:49 MST 2015 get message [newMessage] at Sun Dec 06 23:55:54 MST 2015 Getting data from SOR......

Actualisation du cache

Maintenant, nous apprécions la vitesse et la commodité qu'un cache nous offre, et EhCache est assez agréable pour se rafraîchir tout seul toutes les 10 secondes. Mais que se passe-t-il si nous souhaitons qu'il soit actualisé immédiatement après la mise à jour de notre SOR ? EhCache Spring Annotation propose @TriggersRemove pour supprimer les clés spécifiées du cache lorsque la méthode annotée est appelée. Dans notre API de service de messagerie, le message mis en cache doit être supprimé du cache lorsque setMessage est appelé. Ainsi, la prochaine fois qu'une requête getMessage arrivera, le cache récupérera un nouvel enregistrement à partir du SOR :

 @Cacheable( cacheName = "messageCache", keyGenerator = @KeyGenerator ( // method name is not included in cache key to work with @TriggersRemove name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } @TriggersRemove( cacheName = "messageCache", keyGenerator = @KeyGenerator ( name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public void setMessage( @PartialCacheKey Integer id, String message ) { messages.put( id, message ); }

Un générateur de clé est utilisé par le gestionnaire de cache pour générer la clé de cache. Une liste de générateurs de clé de cache prédéfinis peut être trouvée ici. Par défaut, @KeyGenerator à la fois le nom de la méthode et les paramètres transmis pour générer la clé de cache. Mais comme nous voulons que la méthode setMessage génère la même clé que getMessage et supprime la valeur en cache associée à cette clé, nous devons utiliser uniquement l'ID de message comme clé et éliminer le nom de la méthode pour la génération de clé. Nous définissons donc la propriété includeMethod du générateur de clé sur false pour les deux méthodes. De plus, puisque setMessage a deux arguments, nous utilisons l'annotation @PartialCacheKey d' @PartialCacheKey sur le paramètre id pour spécifier que c'est le seul qui doit être utilisé par le générateur de clé. Enfin, rappelez-vous que nous avons configuré un cache dédié, messageCache , pour ce type de ressource, donc utiliser uniquement l'ID de la clé ne présente aucun risque de conflit avec d'autres types de ressources.

Maintenant, si nous faisons plusieurs requêtes HTTP pour le message avec ID=1, comme suit :

 HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage1 HTTP GET:http://localhost:8080/EhCacheExample/message/1 HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage2 HTTP GET:http://localhost:8080/EhCacheExample/message/1

La console affichera :

 set message [newMessage1] at Tue Dec 08 17:53:44 MST 2015 get message [newMessage1] at Tue Dec 08 17:53:47 MST 2015 Getting data from SOR...... set message [newMessage2] at Tue Dec 08 17:53:50 MST 2015 get message [newMessage2] at Tue Dec 08 17:53:53 MST 2015 Getting data from SOR......

Conclusion

La structure finale du projet ressemble à ceci :

Structure finale du projet.

Dans cet exemple, nous avons d'abord créé une simple application Web Spring MVC RESTful. Sans modifier une seule ligne du code de l'application existante, nous avons ensuite intégré EhCache de manière transparente dans l'application à l'aide des annotations EhCache Spring. Nous avons démontré que EhCache Spring Annotations est à la fois facile à installer (en ajoutant sa dépendance Maven) et élégant à utiliser (en ajoutant des annotations aux méthodes).

Lectures complémentaires

La documentation EhCache peut être trouvée ici et la documentation EhCache Spring Annotations est ici.

Consultez également l'exemple de projet décrit dans cet article sur GitHub.