Memorarea în cache în primăvară cu adnotări EhCache
Publicat: 2022-03-11EhCache este un cache Java pur utilizat pe scară largă, care poate fi integrat cu ușurință cu cele mai populare cadre Java, cum ar fi Spring și Hibernate. Este adesea considerat a fi cea mai convenabilă alegere pentru aplicațiile Java, deoarece poate fi integrat cu ușurință în proiecte. În special:
- Poate fi configurat prin simpla includere a JAR-ului în proiectul dumneavoastră. Nu sunt necesari pași suplimentari de instalare.
- Se rulează în același proces cu aplicația, deci este rapid. Nu este necesar niciun serviciu suplimentar pentru a rula.
Pe scurt, EhCache este o alegere excelentă pentru orice aplicație pur-Java.
În plus, EhCache Spring Annotations permite integrarea perfectă în orice aplicație Spring prin simpla adăugare de adnotări la metodele care pot fi stocate în cache, fără a modifica implementările metodei.
În timp ce EhCache oferă API-uri simple și bogate pentru a manipula cache-ul în mod programatic, acest articol se concentrează în principal pe îmbunătățirea aplicațiilor Spring într-un mod mai puțin intruziv cu EhCache Spring Annotations. Vom configura un proiect Spring MVC și vom implementa un serviciu web RESTful în Tomcat. Apoi, EhCache va fi integrat serviciului web.
rezumatul proiectului
Vom demonstra Adnotările EhCache în contextul unui exemplu de proiect. Vom configura un serviciu web bazat pe Spring MVC găzduit pe un server Tomcat 8.
Am dezvoltat proiectul în Eclipse, care poate fi instalat urmând instrucțiunile de aici.
Cea mai recentă versiune stabilă a Tomcat, Tomcat 8, poate fi descărcată de aici.
Desigur, aceste platforme specifice nu sunt o cerință pentru EhCache; puteți alege oricând IDE-ul și serverul preferat.
JAR-ul EhCache Spring Annotations este disponibil aici. După cum putem vedea, există două JAR-uri pentru fiecare versiune: unul cu dependențe și unul fără. Cel cu dependențe include și EhCache 2 și Spring 3, care sunt necesare pentru ca adnotările EhCache să funcționeze. Este mai ușor de configurat dacă îl descarcăm pe cel cu dependențe și îl adăugăm la calea noastră de construire.
EhCache Spring Annotations este compatibil și cu Spring 4, deși trebuie configurat separat. Nu este clar dacă proiectul va sprijini EhCache 3 în viitorul apropiat. Pentru cei care folosesc sau intenționează să folosească EhCache 3, abordarea adnotărilor discutată în acest articol nu este recomandată.
În cele din urmă, vom folosi Maven pentru a gestiona totul. Maven vine pre-ambalat cu majoritatea instalărilor Eclipse, dar poate fi obținut și aici. Dependențele Spring MVC și EhCache Spring Annotations pot fi adăugate destul de ușor, așa cum se arată mai târziu în acest articol.
Configurarea proiectului
Dacă nu ai pus niciodată la punct un proiect de primăvară, s-ar putea să găsești și postarea lui Stefan Varga pe acest subiect informativă.
Pentru această demonstrație, vom crea un proiect de bază folosind arhetipul Maven maven-archetype-webapp
. Structura generală a fișierului va arăta astfel:
Creați un director, src/main/java
, cu trei pachete: com.toptal.blog
, com.toptal.blog.cache
și com.toptal.blog.service
. Sursa aplicației noastre va intra în aceste pachete, așa cum este descris mai jos.
Să definim un servlet Tomcat numit „springrest” în 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>
Dacă nu se specifică în mod explicit altfel, un Spring MVC DispatcherServlet
va căuta un fișier de configurare XML numit {servlet-name}-servlet.xml
în directorul WEB-INF
. Să creăm un fișier de configurare numit springrest-servlet.xml
. Pentru a activa metodele de control de proces Spring adnotate cu @RequestMapping
, să adăugăm pur și simplu <mvc:annotation-driven />
la acest fișier. De asemenea, să definim pachetul de bază pentru ca Spring să scaneze și să înregistreze automat bean-urile adăugând <context:component-scan base-package="com.toptal.blog" />
. Configurația springrest-servlet.xml
devine:
<beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>
Un serviciu web simplu RESTful
Acum că proiectul nostru este configurat corect, să implementăm un simplu API „serviciu de mesaje”. În pachetul nostru de bază, project.toptal.blog
, vom adăuga SpringRestControllerWithEhCache.java
, cu o metodă GET pentru a obține un mesaj după ID și o metodă POST pentru a seta un mesaj după 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; } }
Vom defini clasa MessageService
în com.toptal.blog.service
. Acesta va accesa mesajele stocate în Sistemul nostru de Înregistrări (SOR). Într-o aplicație de producție, SOR ar fi ceva ca o bază de date relațională. Pentru simplitate, vom folosi 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 ); } }
Acum, dacă exportăm proiectul ca WAR și îl implementăm în Tomcat, ar trebui să putem seta un mesaj, de exemplu „test_message”, pentru ID=1, creând o solicitare HTTP POST la http://localhost:8080/EhCacheExample/message/set/1/test_message
. Apoi, ar trebui să putem obține „test_message” înapoi cu o solicitare HTTP GET la http://localhost:8080/EhCacheExample/message/1
. Am folosit Insomnia ca un client REST convenabil pentru a-mi face testul.
Conectați EhCache Spring Annotations
Acum hai să punem EhCache să lucreze pentru noi. Este nevoie de doar câțiva pași rapidi pentru a configura proiectul nostru pentru a rula corect EhCache.
Pasul 1: Actualizați dependențele pentru a utiliza EhCache Spring Annotations
Adăugați dependența EhCache Spring Annotations în pom.xml
lui Maven:
<!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>
Pasul 2: Configurați un Manager Cache Personalizat
Spring are încorporat un manager de cache EhCache, org.springframework.cache.ehcache.EhCacheManagerFactoryBean
. Acest lucru este potrivit pentru majoritatea situațiilor de stocare în cache, dar am considerat că definirea unui manager de cache personalizat este utilă, deoarece îmi permite să controlez memoria cache fie programatic, fie cu adnotări, folosind același manager de cache. Acest articol se concentrează pe adnotări, dar haideți să definim un manager cache personalizat, astfel încât să fim pregătiți în cazul în care avem nevoie de el. Dacă preferați să rămâneți cu managerul cache implicit , puteți sări peste acest pas.

Vom defini noua clasă în 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 * } * */ }
Activați-l prin actualizarea springrest-servlet.xml
după cum urmează:
... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...
Pasul 3: Configurați EhCache
În cele din urmă, creați fișierul de configurare ehcache.xml
în classpath. În mod implicit, Eclipse va include src/main/resources
în classpath și vom plasa fișierul aici. Acest fișier este necesar pentru ca EhCache să funcționeze corect. Acesta definește numele cache-ului și unele proprietăți ale fiecărui cache, cum ar fi 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>
Pasul 4: Testați memoria cache
Acum, cu totul configurat și gata de funcționare, utilizarea EhCache ar trebui să fie o muncă ușoară și fericită. Putem adăuga pur și simplu @Cacheable
la metoda sau clasa pe care dorim să o punem în cache. De exemplu, am adăugat @Cacheable
la metoda getMessage
din MessageService
. Este atât de ușor!
@Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); }
Pentru a testa dacă cache-ul nostru funcționează, putem crea un mesaj pentru ID=1 emitând o solicitare HTTP POST la http://localhost:8080/EhCacheExample/message/set/1/newMessage
și apoi obținem mesajul pentru ID= 1 de mai multe ori, cu solicitări GET către http://localhost:8080/EhCacheExample/message/1
. După cum puteți vedea în rezultatul consolei de mai jos, serviciul web solicită SOR să primească mesajul prima dată când solicităm mesajul, dar nu pentru următoarele două solicitări, returnând în schimb mesajul stocat în cache. Deoarece am definit timeToLiveSeconds
ca fiind 10, serviciul web apelează SOR pentru a primi mesajul din nou după 10 secunde:
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......
Reîmprospătarea memoriei cache
Acum, ne bucurăm de viteza și comoditatea pe care ni le oferă un cache, iar EhCache este suficient de frumos pentru a se reîmprospăta singur la fiecare 10 secunde. Dar dacă am dori să îl reîmprospătăm imediat după actualizarea SOR-ului nostru? EhCache Spring Annotation oferă @TriggersRemove
pentru a elimina cheile specificate din cache atunci când este apelată metoda adnotată. În API-ul serviciului nostru de mesaje, mesajul stocat în cache ar trebui să fie eliminat din cache atunci când este apelat setMessage
. Astfel, data viitoare când vine o solicitare getMessage
, memoria cache va prelua o înregistrare nouă din 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 generator de chei este folosit de managerul de cache pentru a genera cheia de cache. O listă de generatoare de chei cache predefinite poate fi găsită aici. În mod implicit, @KeyGenerator
consumă atât numele metodei, cât și parametrii trecuți pentru a genera cheia cache. Dar din moment ce dorim ca metoda setMessage
să genereze aceeași cheie ca și getMessage
și să ștergem valoarea stocată în cache asociată cu acea cheie, trebuie să folosim doar ID-ul mesajului ca cheie și să eliminăm numele metodei pentru generarea cheii. Prin urmare, setăm proprietatea includeMethod
a generatorului de chei să fie false
pentru ambele metode. De asemenea, deoarece setMessage
are două argumente, folosim adnotarea @PartialCacheKey a lui @PartialCacheKey
pe parametrul id
pentru a specifica că este singurul care ar trebui utilizat de generatorul de chei. În cele din urmă, amintiți-vă că am configurat un cache dedicat, messageCache
, pentru acest tip de resursă, astfel încât utilizarea doar a ID-ului pentru cheie nu prezintă pericol de conflicte cu alte tipuri de resurse.
Acum, dacă facem mai multe solicitări HTTP pentru mesajul cu ID=1, după cum urmează:
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
Consola va afișa:
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......
Concluzie
Structura finală a proiectului arată astfel:
În acest exemplu, am creat mai întâi o aplicație web Spring MVC RESTful simplă. Fără a modifica măcar o singură linie a codului aplicației existente, am integrat apoi EhCache în aplicație folosind EhCache Spring Annotations. Am demonstrat că EhCache Spring Annotations este atât ușor de instalat (prin adăugarea dependenței sale de Maven), cât și elegant de utilizat (prin adăugarea de adnotări la metode).
Lectură suplimentară
Documentația EhCache poate fi găsită aici, iar documentația EhCache Spring Annotations este aici.
De asemenea, consultați exemplul de proiect descris în acest articol pe GitHub.