Memorizzazione nella cache in primavera con le annotazioni EhCache
Pubblicato: 2022-03-11EhCache è una cache Java pura ampiamente utilizzata che può essere facilmente integrata con i framework Java più diffusi, come Spring e Hibernate. È spesso considerata la scelta più conveniente per le applicazioni Java poiché può essere facilmente integrata nei progetti. In particolare:
- Può essere impostato semplicemente includendo il JAR nel tuo progetto. Non sono necessari ulteriori passaggi di installazione.
- Funziona nello stesso processo con l'applicazione, quindi è veloce. Non è richiesto alcun servizio aggiuntivo per l'esecuzione.
In breve, EhCache è un'ottima scelta per qualsiasi applicazione Java puro.
Inoltre, EhCache Spring Annotations consente una perfetta integrazione in qualsiasi applicazione Spring semplicemente aggiungendo annotazioni ai metodi memorizzabili nella cache, senza modificare le implementazioni del metodo.
Sebbene EhCache fornisca API complete e dirette per manipolare la cache a livello di codice, questo articolo si concentra principalmente sul potenziamento delle applicazioni Spring in un modo meno invadente con EhCache Spring Annotations. Imposteremo un progetto Spring MVC e implementeremo un servizio Web RESTful in Tomcat. Quindi, EhCache verrà integrato nel servizio web.
Panoramica del progetto
Dimostreremo le annotazioni EhCache nel contesto di un progetto di esempio. Imposteremo un servizio Web basato su MVC Spring ospitato su un server Tomcat 8.
Ho sviluppato il progetto in Eclipse, che può essere installato seguendo le istruzioni qui.
L'ultima versione stabile di Tomcat, Tomcat 8, può essere scaricata qui.
Naturalmente, queste piattaforme specifiche non sono un requisito per EhCache; puoi sempre scegliere il tuo IDE e server preferito.
Il JAR delle annotazioni primaverili di EhCache è disponibile qui. Come possiamo vedere, ci sono due JAR per ogni versione: uno con dipendenze e uno senza. Quello con dipendenze include anche EhCache 2 e Spring 3, necessari per il funzionamento delle annotazioni EhCache. È più facile da configurare se scarichiamo quello con le dipendenze e lo aggiungiamo al nostro percorso di compilazione.
EhCache Spring Annotations è compatibile anche con Spring 4, sebbene debba essere configurato separatamente. Non è chiaro se il progetto supporterà EhCache 3 nel prossimo futuro. Per coloro che utilizzano o intendono utilizzare EhCache 3, l'approccio di annotazione discusso in questo articolo non è consigliato.
Infine, utilizzeremo Maven per gestire tutto. Maven viene fornito preconfezionato con la maggior parte delle installazioni di Eclipse, ma può anche essere ottenuto qui. Le dipendenze Spring MVC e EhCache Spring Annotations possono essere aggiunte abbastanza facilmente, come mostrato più avanti in questo articolo.
Configurazione del progetto
Se non hai mai avviato un progetto primaverile, potresti anche trovare informativo il post di Stefan Varga sull'argomento.
Per questa dimostrazione, imposteremo un progetto di base utilizzando l'archetipo Maven maven-archetype-webapp . La struttura complessiva del file sarà simile a questa:
Crea una directory, src/main/java , con tre pacchetti: com.toptal.blog , com.toptal.blog.cache e com.toptal.blog.service . La fonte dell'applicazione andrà in questi pacchetti, come descritto più avanti.
Definiamo un servlet Tomcat chiamato "springrest" in 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> Se non diversamente specificato, Spring MVC DispatcherServlet cercherà un file di configurazione XML denominato {servlet-name}-servlet.xml nella directory WEB-INF . Creiamo un file di configurazione chiamato springrest-servlet.xml . Per abilitare i metodi del controller di processo Spring annotati con @RequestMapping , aggiungiamo semplicemente <mvc:annotation-driven /> a questo file. Inoltre, definiamo il pacchetto base per Spring per scansionare e registrare automaticamente i bean aggiungendo <context:component-scan base-package="com.toptal.blog" /> . La configurazione springrest-servlet.xml diventa:
<beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>Un semplice servizio Web RESTful
Ora che il nostro progetto è correttamente configurato, implementiamo una semplice API di “servizio di messaggistica”. Nel nostro pacchetto base, project.toptal.blog , aggiungeremo SpringRestControllerWithEhCache.java , con un metodo GET per ottenere un messaggio per ID e un metodo POST per impostare un messaggio per 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; } } Definiremo la classe MessageService in com.toptal.blog.service . Accederà ai messaggi archiviati nel nostro System of Records (SOR). In un'app di produzione, il SOR sarebbe qualcosa come un database relazionale. Per semplicità useremo una 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 ); } } Ora, se esportiamo il progetto come WAR e lo distribuiamo in Tomcat, dovremmo essere in grado di impostare un messaggio, ad esempio "test_message", per ID=1, creando una richiesta HTTP POST su http://localhost:8080/EhCacheExample/message/set/1/test_message . Dovremmo quindi essere in grado di recuperare "test_message" con una richiesta HTTP GET su http://localhost:8080/EhCacheExample/message/1 . Ho usato Insomnia come comodo client REST per fare il mio test.
Collega le annotazioni primaverili di EhCache
Ora facciamo in modo che EhCache funzioni per noi. Bastano pochi rapidi passaggi per configurare il nostro progetto per eseguire correttamente EhCache.
Passaggio 1: aggiorna le dipendenze per utilizzare le annotazioni primaverili di EhCache
Aggiungi la dipendenza EhCache Spring Annotations in pom.xml di Maven:
<!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>Passaggio 2: imposta un gestore cache personalizzato
Spring ha un gestore di cache EhCache integrato, org.springframework.cache.ehcache.EhCacheManagerFactoryBean . Questo è adatto per la maggior parte delle situazioni di memorizzazione nella cache, ma ho trovato utile definire un gestore di cache personalizzato perché mi consente di controllare la cache a livello di codice o con annotazioni, utilizzando lo stesso gestore di cache. Questo articolo si concentra sulle annotazioni, ma andiamo avanti e definiamo un gestore di cache personalizzato in modo da essere pronti nel caso ne avessimo bisogno. Se preferisci rimanere con il gestore della cache predefinito , puoi saltare questo passaggio.

Definiremo la nuova classe in 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 * } * */ } Abilitalo aggiornando springrest-servlet.xml come segue:
... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...Passaggio 3: configura EhCache
Infine, crea il file di configurazione EhCache ehcache.xml nel percorso classe. Per impostazione predefinita, Eclipse includerà src/main/resources nel percorso di classe e collocheremo il file qui. Questo file è necessario per il corretto funzionamento di EhCache. Definisce i nomi della cache e alcune proprietà di ciascuna cache, come 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>Passaggio 4: testare la cache
Ora, con tutto pronto e pronto per l'uso, l'utilizzo di EhCache dovrebbe essere un lavoro facile e felice. Possiamo semplicemente aggiungere @Cacheable al metodo o alla classe che vogliamo memorizzare nella cache. Ad esempio, ho aggiunto @Cacheable al metodo getMessage in MessageService . È così facile!
@Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } Per verificare che la nostra cache funzioni, possiamo creare un messaggio per ID=1 inviando una richiesta HTTP POST a http://localhost:8080/EhCacheExample/message/set/1/newMessage , quindi ottenere il messaggio per ID= 1 più volte, con richieste GET a http://localhost:8080/EhCacheExample/message/1 . Come puoi vedere nell'output della console di seguito, il servizio Web chiede al SOR di ricevere il messaggio la prima volta che richiediamo il messaggio, ma non per le due richieste successive, restituendo invece il messaggio memorizzato nella cache. Poiché abbiamo definito timeToLiveSeconds come 10, il servizio Web chiama il SOR per ricevere nuovamente il messaggio dopo 10 secondi:
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......Aggiornamento della cache
Ora ci stiamo godendo la velocità e la comodità che ci offre una cache ed EhCache è abbastanza carino da aggiornarsi da solo ogni 10 secondi. Ma cosa succede se vorremmo aggiornarlo immediatamente dopo l'aggiornamento del nostro SOR? EhCache Spring Annotation offre @TriggersRemove per rimuovere le chiavi specificate dalla cache quando viene chiamato il metodo annotato. Nella nostra API del servizio messaggi, il messaggio memorizzato nella cache deve essere rimosso dalla cache quando viene chiamato setMessage . Pertanto, la prossima volta che arriva una richiesta getMessage , la cache recupererà un nuovo record dal 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 generatore di chiavi viene utilizzato dal gestore della cache per generare la chiave della cache. Un elenco di generatori di chiavi cache predefiniti è disponibile qui. Per impostazione predefinita, @KeyGenerator sia il nome del metodo che i parametri passati per generare la chiave della cache. Ma poiché vogliamo che il metodo setMessage generi la stessa chiave di getMessage ed elimini il valore memorizzato nella cache associato a quella chiave, dobbiamo utilizzare solo l'ID messaggio come chiave ed eliminare il nome del metodo per la generazione della chiave. Pertanto, impostiamo la proprietà includeMethod del generatore di chiavi su false per entrambi i metodi. Inoltre, poiché setMessage ha due argomenti, utilizziamo l'annotazione @PartialCacheKey di @PartialCacheKey sul parametro id per specificare che è l'unico che dovrebbe essere utilizzato dal generatore di chiavi. Infine, ricordiamo che abbiamo configurato una cache dedicata, messageCache , per questo tipo di risorsa, quindi utilizzare solo l'ID per la chiave non presenta alcun pericolo di conflitti con altri tipi di risorse.
Ora, se eseguiamo diverse richieste HTTP per il messaggio con ID=1, come segue:
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/1La console mostrerà:
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......Conclusione
La struttura del progetto finale si presenta così:
In questo esempio, abbiamo prima creato una semplice applicazione Web Spring MVC RESTful. Senza modificare nemmeno una riga del codice dell'applicazione esistente, abbiamo quindi integrato senza problemi EhCache nell'applicazione utilizzando EhCache Spring Annotations. Abbiamo dimostrato che EhCache Spring Annotations è sia facile da installare (aggiungendo la sua dipendenza Maven) che elegante da usare (aggiungendo annotazioni ai metodi).
Ulteriori letture
La documentazione di EhCache può essere trovata qui e la documentazione di EhCache Spring Annotations è qui.
Inoltre, controlla il progetto di esempio descritto in questo articolo su GitHub.
