Buforowanie na wiosnę z adnotacjami EhCache
Opublikowany: 2022-03-11EhCache to szeroko stosowana, czysta pamięć podręczna Java, którą można łatwo zintegrować z najpopularniejszymi frameworkami Java, takimi jak Spring i Hibernate. Często jest uważany za najwygodniejszy wybór dla aplikacji Java, ponieważ można go łatwo zintegrować z projektami. W szczególności:
- Można go skonfigurować, po prostu dołączając plik JAR do swojego projektu. Nie są wymagane żadne dodatkowe czynności instalacyjne.
- Działa w tym samym procesie co aplikacja, więc jest szybki. Do uruchomienia nie jest wymagana żadna dodatkowa usługa.
Krótko mówiąc, EhCache to świetny wybór dla każdej aplikacji w czystej Javie.
Dodatkowo, EhCache Spring Annotations umożliwia bezproblemową integrację z dowolną aplikacją Spring poprzez proste dodawanie adnotacji do metod, które można przechowywać w pamięci podręcznej, bez modyfikowania implementacji metod.
Chociaż EhCache zapewnia proste, rozbudowane interfejsy API do programowej manipulacji pamięcią podręczną, ten artykuł koncentruje się głównie na ulepszaniu aplikacji Spring w mniej inwazyjny sposób dzięki adnotacjom EhCache Spring. Skonfigurujemy projekt Spring MVC i wdrożymy usługę sieciową RESTful w Tomcat. Następnie EhCache zostanie zintegrowany z usługą sieciową.
Przegląd projektu
Zademonstrujemy adnotacje EhCache w kontekście przykładowego projektu. Skonfigurujemy usługę sieciową opartą na Spring MVC hostowaną na serwerze Tomcat 8.
Projekt opracowałem w środowisku Eclipse, które można zainstalować zgodnie z instrukcjami tutaj.
Najnowszą stabilną wersję Tomcata, Tomcat 8, można pobrać tutaj.
Oczywiście te konkretne platformy nie są wymagane dla EhCache; zawsze możesz wybrać swoje ulubione IDE i serwer.
Plik JAR z adnotacjami EhCache Spring jest dostępny tutaj. Jak widać, dla każdej wersji są dwa JAR-y: jeden z zależnościami i jeden bez. Ta z zależnościami zawiera również EhCache 2 i Spring 3, które są wymagane do działania adnotacji EhCache. Łatwiej jest skonfigurować, jeśli pobierzemy ten z zależnościami i dodamy go do naszej ścieżki budowania.
Adnotacje EhCache Spring są również zgodne z Spring 4, chociaż należy je skonfigurować osobno. Nie jest jasne, czy projekt będzie obsługiwał EhCache 3 w najbliższej przyszłości. Dla tych, którzy używają lub zamierzają używać EhCache 3, podejście do adnotacji omówione w tym artykule nie jest zalecane.
Wreszcie będziemy używać Mavena do zarządzania wszystkim. Maven jest dostarczany z większością instalacji Eclipse, ale można go również uzyskać tutaj. Zależności Spring MVC i EhCache Spring Annotations można dość łatwo dodać, jak pokazano w dalszej części tego artykułu.
Konfiguracja projektu
Jeśli nigdy wcześniej nie zakładałeś projektu wiosennego, pouczający może być również post Stefana Vargi na ten temat.
Na potrzeby tej demonstracji stworzymy podstawowy projekt przy użyciu archetypu Mavena maven-archetype-webapp
. Ogólna struktura plików będzie wyglądać tak:
Utwórz katalog src/main/java
z trzema pakietami: com.toptal.blog
, com.toptal.blog.cache
i com.toptal.blog.service
. Nasze źródło aplikacji zostanie umieszczone w tych pakietach, jak opisano poniżej.
Zdefiniujmy serwlet Tomcata o nazwie „springrest” w 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>
O ile wyraźnie nie określono inaczej, Spring MVC DispatcherServlet
będzie szukał pliku konfiguracyjnego XML o nazwie {servlet-name}-servlet.xml
w katalogu WEB-INF
. Utwórzmy plik konfiguracyjny o nazwie springrest-servlet.xml
. Aby włączyć metody kontrolera procesów Spring z adnotacjami @RequestMapping
, dodajmy po prostu <mvc:annotation-driven />
do tego pliku. Zdefiniujmy również pakiet podstawowy dla Spring, aby automatycznie skanował i rejestrował ziarna, dodając <context:component-scan base-package="com.toptal.blog" />
. springrest-servlet.xml
staje się:
<beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>
Prosta usługa sieciowa RESTful
Teraz, gdy nasz projekt jest już poprawnie skonfigurowany, zaimplementujmy proste API „usługi wiadomości”. W naszym podstawowym pakiecie project.toptal.blog
dodamy SpringRestControllerWithEhCache.java
z jedną metodą GET do pobierania wiadomości według identyfikatora i jedną metodą POST do ustawiania wiadomości według identyfikatora:
@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; } }
Zdefiniujemy klasę com.toptal.blog.service
MessageService
Będzie uzyskiwać dostęp do wiadomości przechowywanych w naszym systemie ewidencji (SOR). W aplikacji produkcyjnej SOR byłby czymś w rodzaju relacyjnej bazy danych. Dla uproszczenia użyjemy 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 ); } }
Teraz, jeśli wyeksportujemy projekt jako WAR i wdrożymy go w Tomcat, powinniśmy być w stanie ustawić komunikat, na przykład „test_message”, dla ID=1, tworząc żądanie HTTP POST na http://localhost:8080/EhCacheExample/message/set/1/test_message
. Powinniśmy wtedy móc odzyskać „test_message” za pomocą żądania HTTP GET pod http://localhost:8080/EhCacheExample/message/1
. Do wykonania testu użyłem Insomni jako wygodnego klienta REST.
Podłącz adnotacje sprężyn EhCache
Teraz niech EhCache pracuje dla nas. Wystarczy kilka szybkich kroków, aby skonfigurować nasz projekt do poprawnego działania EhCache.
Krok 1: Zaktualizuj zależności, aby korzystać z adnotacji EhCache Spring
Dodaj zależność EhCache Spring Annotations w pom.xml
Mavena:
<!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>
Krok 2: Skonfiguruj niestandardowego menedżera pamięci podręcznej
Spring ma wbudowany menedżer pamięci podręcznej org.springframework.cache.ehcache.EhCacheManagerFactoryBean
, org.springframework.cache.ehcache.EhCacheManagerFactoryBean . Jest to odpowiednie dla większości sytuacji związanych z buforowaniem, ale uważam, że zdefiniowanie niestandardowego menedżera pamięci podręcznej jest przydatne, ponieważ pozwala mi kontrolować pamięć podręczną programowo lub za pomocą adnotacji przy użyciu tego samego menedżera pamięci podręcznej. Ten artykuł koncentruje się na adnotacjach, ale zdefiniujmy niestandardowego menedżera pamięci podręcznej, abyśmy byli gotowi na wypadek, gdybyśmy go potrzebowali. Jeśli wolisz pozostać przy domyślnym menedżerze pamięci podręcznej , możesz pominąć ten krok.

Zdefiniujemy nową klasę w 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 * } * */ }
Włącz ją, aktualizując springrest-servlet.xml
w następujący sposób:
... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...
Krok 3: Skonfiguruj EhCache
Na koniec utwórz plik konfiguracyjny ehcache.xml
w ścieżce klasy. Domyślnie Eclipse będzie zawierać src/main/resources
w ścieżce klas, a my umieścimy plik tutaj. Ten plik jest wymagany do prawidłowego działania EhCache. Definiuje nazwy pamięci podręcznej i niektóre właściwości każdej pamięci podręcznej, takie jak 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>
Krok 4: Przetestuj pamięć podręczną
Teraz, gdy wszystko jest skonfigurowane i gotowe do pracy, korzystanie z EhCache powinno być łatwą i przyjemną pracą. Możemy po prostu dodać @Cacheable
do metody lub klasy, którą chcemy buforować. Na przykład dodałem @Cacheable
do metody getMessage
w MessageService
. To jest takie proste!
@Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); }
Aby sprawdzić, czy nasza pamięć podręczna działa, możemy utworzyć wiadomość dla ID=1, wysyłając żądanie HTTP POST pod http://localhost:8080/EhCacheExample/message/set/1/newMessage
, a następnie otrzymać wiadomość dla ID= 1 wielokrotnie, z żądaniami GET do http://localhost:8080/EhCacheExample/message/1
. Jak widać w danych wyjściowych konsoli poniżej, usługa sieciowa prosi SOR o pobranie wiadomości przy pierwszym żądaniu wiadomości, ale nie w przypadku następnych dwóch żądań, zwracając zamiast tego wiadomość z pamięci podręcznej. Ponieważ zdefiniowaliśmy timeToLiveSeconds
na 10, usługa sieci Web wywołuje SOR, aby ponownie otrzymać wiadomość po 10 sekundach:
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......
Odświeżanie pamięci podręcznej
Teraz cieszymy się szybkością i wygodą, jaką daje nam pamięć podręczna, a EhCache jest na tyle fajny, że odświeża się samoczynnie co 10 sekund. Ale co by było, gdybyśmy chcieli go odświeżyć natychmiast po aktualizacji naszego SOR? EhCache Spring Adnotation oferuje @TriggersRemove
, aby usunąć określone klucze z pamięci podręcznej, gdy wywoływana jest metoda z adnotacjami. W naszym interfejsie API usługi wiadomości zbuforowaną wiadomość należy usunąć z pamięci podręcznej po setMessage
. Dlatego następnym razem, gdy nadejdzie żądanie getMessage
, pamięć podręczna pobierze nowy rekord z 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 ); }
Generator kluczy jest używany przez menedżera pamięci podręcznej do generowania klucza pamięci podręcznej. Listę wstępnie zdefiniowanych generatorów kluczy pamięci podręcznej można znaleźć tutaj. Domyślnie @KeyGenerator
zużywa zarówno nazwę metody, jak i przekazane parametry w celu wygenerowania klucza pamięci podręcznej. Ale ponieważ chcemy, aby metoda setMessage
generowała ten sam klucz, co getMessage
i usuwała zbuforowaną wartość skojarzoną z tym kluczem, musimy użyć jako klucza tylko identyfikatora wiadomości i wyeliminować nazwę metody do generowania klucza. Dlatego ustawiamy właściwość includeMethod
generatora kluczy na wartość false
dla obu metod. Ponadto, ponieważ setMessage
ma dwa argumenty, używamy adnotacji @PartialCacheKey
w parametrze id
, aby określić, że jest to jedyny, który powinien być używany przez generator kluczy. Na koniec przypomnijmy, że dla tego typu zasobu skonfigurowaliśmy dedykowaną pamięć podręczną messageCache
, więc użycie tylko identyfikatora klucza nie stwarza niebezpieczeństwa konfliktów z innymi typami zasobów.
Teraz, jeśli wykonamy kilka żądań HTTP dla wiadomości o ID=1, w następujący sposób:
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
Konsola pokaże:
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......
Wniosek
Ostateczna struktura projektu wygląda tak:
W tym przykładzie najpierw stworzyliśmy prostą aplikację internetową Spring MVC RESTful. Bez modyfikowania nawet jednej linii istniejącego kodu aplikacji, bezproblemowo zintegrowaliśmy EhCache z aplikacją za pomocą EhCache Spring Annotations. Pokazaliśmy, że EhCache Spring Annotations jest zarówno łatwy w instalacji (poprzez dodanie jego zależności od Maven), jak i elegancki w użyciu (poprzez dodanie adnotacji do metod).
Dalsza lektura
Dokumentację EhCache można znaleźć tutaj, a dokumentacja EhCache Spring Annotations jest tutaj.
Zapoznaj się również z przykładowym projektem opisanym w tym artykule w serwisie GitHub.