在 Spring 中使用 EhCache 註解進行緩存

已發表: 2022-03-11

EhCache 是一種廣泛使用的純 Java 緩存,可以輕鬆與大多數流行的 Java 框架集成,例如 Spring 和 Hibernate。 它通常被認為是 Java 應用程序最方便的選擇,因為它可以輕鬆集成到項目中。 特別是:

  • 只需在項目中包含 JAR 即可設置它。 不需要額外的安裝步驟。
  • 它與應用程序在同一進程中運行,因此速度很快。 運行不需要額外的服務。

簡而言之,EhCache 是任何純 Java 應用程序的絕佳選擇。

此外,EhCache Spring Annotations 允許無縫集成到任何 Spring 應用程序中,只需將註釋添加到可緩存方法,而無需修改方法實現。

EhCache 是一個很好的 Spring 項目緩存解決方案。

雖然 EhCache 提供了直接、豐富的 API 來以編程方式操作緩存,但本文主要側重於使用 EhCache Spring Annotations 以一種不那麼侵入性的方式增強您的 Spring 應用程序。 我們將建立一個 Spring MVC 項目並在 Tomcat 中部署一個 RESTful Web 服務。 然後,EhCache 將被集成到 Web 服務中。

項目概況

我們將在示例項目的上下文中演示 EhCache 註釋。 我們將建立一個託管在 Tomcat 8 服務器上的基於 Spring MVC 的 Web 服務。

我在 Eclipse 中開發了該項目,可以按照此處的說明進行安裝。

可以在此處下載 Tomcat 的最新穩定版本 Tomcat 8。

當然,這些特定平台不是 EhCache 的要求; 您可以隨時選擇自己喜歡的 IDE 和服務器。

EhCache Spring Annotations JAR 可在此處獲得。 正如我們所見,每個版本都有兩個 JAR:一個有依賴關係,一個沒有。 具有依賴項的還包括 EhCache 2 和 Spring 3,它們是 EhCache 註釋工作所必需的。 如果我們下載具有依賴項的文件並將其添加到我們的構建路徑中,則設置會更容易。

EhCache Spring Annotations 也與 Spring 4 兼容,但必須單獨配置。 目前尚不清楚該項目是否會在不久的將來支持 EhCache 3。 對於正在使用或打算使用 EhCache 3 的用戶,不建議使用本文中討論的註釋方法。

最後,我們將使用 Maven 來管理一切。 大多數 Eclipse 安裝都預先打包了 Maven,但也可以在此處獲取。 可以相當容易地添加 Spring MVC 和 EhCache Spring Annotations 依賴項,如本文後面所示。

項目設置

如果您以前從未設置過 Spring 項目,您可能還會發現 Stefan Varga 關於該主題的帖子提供了豐富的信息。

對於這個演示,我們將使用 Maven 原型maven-archetype-webapp設置一個基本項目。 整個文件結構將如下所示:

初始 Spring 項目結構。

創建一個目錄src/main/java ,其中包含三個包: com.toptal.blogcom.toptal.blog.cachecom.toptal.blog.service 。 我們的應用程序源將放在這些包中,如下所述。

讓我們在web.xml中定義一個名為“springrest”的 Tomcat servlet:

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

除非另有明確說明,否則 Spring MVC DispatcherServlet將在WEB-INF目錄中查找名為{servlet-name}-servlet.xml的 XML 配置文件。 讓我們創建一個名為springrest-servlet.xml的配置文件。 要啟用使用@RequestMapping註釋的 Spring 流程控制器方法,我們只需將<mvc:annotation-driven />添加到此文件中。 另外,讓我們通過添加<context:component-scan base-package="com.toptal.blog" />來定義 Spring 的基本包以自動掃描和註冊 bean。 springrest-servlet.xml配置變為:

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

一個簡單的 RESTful Web 服務

現在我們的項目已正確配置,讓我們實現一個簡單的“消息服務”API。 在我們的基礎包project.toptal.blog中,我們將添加SpringRestControllerWithEhCache.java ,其中一個 GET 方法通過 ID 獲取消息,一個 POST 方法通過 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; } }

我們將在com.toptal.blog.service中定義MessageService類。 它將訪問存儲在我們的記錄系統 (SOR) 中的消息。 在生產應用程序中,SOR 類似於關係數據庫。 為簡單起見,我們將使用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 ); } }

現在,如果我們將項目導出為 WAR 並將其部署在 Tomcat 中,我們應該能夠通過在http://localhost:8080/EhCacheExample/message/set/1/test_message處創建 HTTP POST 請求來為 ID=1 設置消息,例如“test_message” http://localhost:8080/EhCacheExample/message/set/1/test_message 。 然後,我們應該能夠通過http://localhost:8080/EhCacheExample/message/1的 HTTP GET 請求獲取“test_message”。 我使用 Insomnia 作為方便的 REST 客戶端來進行測試。

插入 EhCache Spring 註解

現在讓 EhCache 為我們工作。 只需幾個快速步驟即可配置我們的項目以正確運行 EhCache。

EhCache Spring Annotations 使 EhCache 可以輕鬆無縫地部署到您的應用程序中。

第 1 步:更新依賴項以使用 EhCache Spring Annotations

在 Maven 的pom.xml中添加 EhCache Spring Annotations 依賴項:

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

步驟 2:設置自定義緩存管理器

Spring 有一個內置的 EhCache 緩存管理器org.springframework.cache.ehcache.EhCacheManagerFactoryBean 。 這適用於大多數緩存情況,但我發現定義自定義緩存管理器很有用,因為它允許我使用相同的緩存管理器以編程方式或通過註釋控制緩存。 本文重點介紹註解,但讓我們繼續定義一個自定義緩存管理器,以便在需要時做好準備。 如果您更喜歡使用默認緩存管理器,則可以跳過此步驟。

我們將在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 * } * */ }

通過更新springrest-servlet.xml來啟用它,如下所示:

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

第三步:配置 EhCache

最後,在類路徑中創建 EhCache 配置文件ehcache.xml 。 默認情況下,Eclipse 會在類路徑中包含src/main/resources ,我們將把文件放在這裡。 此文件是 EhCache 正常運行所必需的。 它定義了緩存名稱和每個緩存的一些屬性,例如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>

第 4 步:測試緩存

現在,一切準備就緒,使用 EhCache 應該是一件輕鬆愉快的工作。 我們可以簡單地將@Cacheable添加到我們想要緩存的方法或類中。 例如,我在MessageServicegetMessage方法中添加了@Cacheable 。 就這麼容易!

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

為了測試我們的緩存是否正常工作,我們可以通過在http://localhost:8080/EhCacheExample/message/set/1/newMessage發出 HTTP POST 請求來創建 ID=1 的消息,然後獲取 ID= 的消息1 多次,對http://localhost:8080/EhCacheExample/message/1的 GET 請求。 正如您在下面的控制台輸出中看到的那樣,Web 服務在我們第一次請求消息時要求 SOR 獲取消息,但對於接下來的兩個請求則沒有,而是返回緩存的消息。 因為我們將timeToLiveSeconds定義為 10,所以 Web 服務調用 SOR 以在 10 秒後再次獲取消息:

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

刷新緩存

現在,我們正在享受緩存給我們帶來的速度和便利,而 EhCache 足夠好,可以每 10 秒自行刷新一次。 但是,如果我們希望在 SOR 更新後立即刷新它呢? EhCache Spring Annotation 提供@TriggersRemove以在調用帶註釋的方法時從緩存中刪除指定的鍵。 在我們的消息服務 API 中,應該在調用setMessage時從緩存中刪除緩存的消息。 因此,下一次getMessage請求進入時,緩存將從 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 ); }

緩存管理器使用密鑰生成器來生成緩存密鑰。 可以在此處找到預定義的緩存密鑰生成器列表。 默認情況下, @KeyGenerator使用方法名和傳入的參數來生成緩存鍵。 但是由於我們希望setMessage方法生成與getMessage相同的 key 並刪除與該 key 關聯的緩存值,因此我們必須僅使用消息 ID 作為 key 並消除 key 生成的方法名稱。 因此,對於這兩種方法,我們將密鑰生成器的includeMethod屬性設置為false 。 此外,由於setMessage有兩個參數,我們在id參數上使用 EhCache 的@PartialCacheKey註釋來指定它是唯一應該由密鑰生成器使用的參數。 最後,回想一下,我們為此資源類型配置了一個專用緩存messageCache ,因此僅將 ID 用作鍵不會存在與其他資源類型發生衝突的危險。

現在,如果我們對 ID=1 的消息進行多次 HTTP 請求,如下所示:

 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

控制台將顯示:

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

結論

最終的項目結構如下所示:

最終項目結構。

在這個示例中,我們首先創建了一個簡單的 Spring MVC RESTful Web 應用程序。 無需修改現有應用程序的任何一行代碼,我們就可以使用 EhCache Spring Annotations 將 EhCache 無縫集成到應用程序中。 我們已經證明 EhCache Spring Annotations 既易於安裝(通過添加其 Maven 依賴項)又易於使用(通過向方法添加註釋)。

延伸閱讀

EhCache 文檔可以在這裡找到,EhCache Spring Annotations 文檔在這裡。

此外,在 GitHub 上查看本文中描述的示例項目。