Spring 整合 Ehcache 管理快取詳解
前言
Ehcache 是一個成熟的快取框架,你可以直接使用它來管理你的快取。
Spring 提供了對快取功能的抽象:即允許繫結不同的快取解決方案(如Ehcache),但本身不直接提供快取功能的實現。它支援註解方式使用快取,非常方便。
本文先通過Ehcache獨立應用的範例來介紹它的基本使用方法,然後再介紹與Spring整合的方法。
概述
Ehcache是什麼?
EhCache 是一個純Java的程式內快取框架,具有快速、精幹等特點。它是Hibernate中的預設快取框架。
Ehcache已經發布了3.1版本。但是本文的講解基於2.10.2版本。
為什麼不使用最新版呢?因為Spring4還不能直接整合Ehcache 3.x。雖然可以通過JCache間接整合,Ehcache也支援JCache,但是個人覺得不是很方便。
安裝
Ehcache
如果你的專案使用maven管理,新增以下依賴到你的pom.xml中。
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.2</version> <type>pom</type> </dependency>
如果你的專案不使用maven管理,請在 Ehcache官網下載地址 下載jar包。
Spring
如果你的專案使用maven管理,新增以下依賴到你的pom.xml中。
spring-context-support
這個jar包中含有Spring對於快取功能的抽象封裝介面。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.1.4.RELEASE</version> </dependency>
Ehcache的使用
HelloWorld範例
接觸一種技術最快最直接的途徑總是一個Hello World例子,畢竟動手實踐印象更深刻,不是嗎?
(1) 在classpath下新增ehcache.xml
新增一個名為helloworld的快取。
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- 磁碟快取位置 --> <diskStore path="java.io.tmpdir/ehcache"/> <!-- 預設快取 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <!-- helloworld快取 --> <cache name="helloworld" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="5" timeToLiveSeconds="5" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"/> </ehcache>
(2) EhcacheDemo.java
Ehcache會自動載入classpath根目錄下名為ehcache.xml檔案。
EhcacheDemo的工作步驟如下:
在EhcacheDemo中,我們引用ehcache.xml宣告的名為helloworld的快取來建立Cache
物件;
然後我們用一個鍵值對來例項化Element
物件;
將Element
物件新增到Cache
;
然後用Cache
的get方法獲取Element
物件。
public class EhcacheDemo { public static void main(String[] args) throws Exception { // Create a cache manager final CacheManager cacheManager = new CacheManager(); // create the cache called "helloworld" final Cache cache = cacheManager.getCache("helloworld"); // create a key to map the data to final String key = "greeting"; // Create a data element final Element putGreeting = new Element(key, "Hello, World!"); // Put the element into the data store cache.put(putGreeting); // Retrieve the data element final Element getGreeting = cache.get(key); // Print the value System.out.println(getGreeting.getObjectValue()); } }
輸出
Hello, World!
Ehcache基本操作
Element
、Cache
、CacheManager
是Ehcache最重要的API。
- Element:快取的元素,它維護著一個鍵值對。
- Cache:它是Ehcache的核心類,它有多個
Element
,並被CacheManager
管理。它實現了對快取的邏輯行為。 - CacheManager:
Cache
的容器物件,並管理著Cache
的生命週期。
建立CacheManager
下面的程式碼列舉了建立CacheManager
的五種方式。
使用靜態方法create()
會以預設配置來建立單例的CacheManager
例項。
newInstance()
方法是一個工廠方法,以預設配置建立一個新的CacheManager
例項。
此外,newInstance()
還有幾個過載函式,分別可以通過傳入String
、URL
、InputStream
引數來載入配置檔案,然後建立CacheManager
例項。
// 使用Ehcache預設配置獲取單例的CacheManager例項 CacheManager.create(); String[] cacheNames = CacheManager.getInstance().getCacheNames(); // 使用Ehcache預設配置新建一個CacheManager例項 CacheManager.newInstance(); String[] cacheNames = manager.getCacheNames(); // 使用不同的配置檔案分別建立一個CacheManager例項 CacheManager manager1 = CacheManager.newInstance("src/config/ehcache1.xml"); CacheManager manager2 = CacheManager.newInstance("src/config/ehcache2.xml"); String[] cacheNamesForManager1 = manager1.getCacheNames(); String[] cacheNamesForManager2 = manager2.getCacheNames(); // 基於classpath下的配置檔案建立CacheManager例項 URL url = getClass().getResource("/anotherconfigurationname.xml"); CacheManager manager = CacheManager.newInstance(url); // 基於檔案流得到配置檔案,並建立CacheManager例項 InputStream fis = new FileInputStream(new File ("src/config/ehcache.xml").getAbsolutePath()); try { CacheManager manager = CacheManager.newInstance(fis); } finally { fis.close(); }
新增快取
需要強調一點,Cache
物件在用addCache
方法新增到CacheManager
之前,是無效的。
使用CacheManager的addCache方法可以根據快取名將ehcache.xml中宣告的cache新增到容器中;它也可以直接將Cache物件新增到快取容器中。
Cache
有多個建構函式,提供了不同方式去載入快取的配置引數。
有時候,你可能需要使用API來動態的新增快取,下面的例子就提供了這樣的範例。
// 除了可以使用xml檔案中配置的快取,你也可以使用API動態增刪快取 // 新增快取 manager.addCache(cacheName); // 使用預設配置新增快取 CacheManager singletonManager = CacheManager.create(); singletonManager.addCache("testCache"); Cache test = singletonManager.getCache("testCache"); // 使用自定義配置新增快取,注意快取未新增進CacheManager之前並不可用 CacheManager singletonManager = CacheManager.create(); Cache memoryOnlyCache = new Cache("testCache", 5000, false, false, 5, 2); singletonManager.addCache(memoryOnlyCache); Cache test = singletonManager.getCache("testCache"); // 使用特定的配置新增快取 CacheManager manager = CacheManager.create(); Cache testCache = new Cache( new CacheConfiguration("testCache", maxEntriesLocalHeap) .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) .eternal(false) .timeToLiveSeconds(60) .timeToIdleSeconds(30) .diskExpiryThreadIntervalSeconds(0) .persistence(new PersistenceConfiguration().strategy(Strategy.LOCALTEMPSWAP))); manager.addCache(testCache);
刪除快取
刪除快取比較簡單,你只需要將指定的快取名傳入removeCache
方法即可。
CacheManager singletonManager = CacheManager.create(); singletonManager.removeCache("sampleCache1");
實現基本快取操作
Cache最重要的兩個方法就是put和get,分別用來新增Element和獲取Element。
Cache還提供了一系列的get、set方法來設定或獲取快取引數,這裡不一一列舉,更多API操作可參考官方API開發手冊。
/** * 測試:使用預設配置或使用指定配置來建立CacheManager * * @author victor zhang */ public class CacheOperationTest { private final Logger log = LoggerFactory.getLogger(CacheOperationTest.class); /** * 使用Ehcache預設配置(classpath下的ehcache.xml)獲取單例的CacheManager例項 */ @Test public void operation() { CacheManager manager = CacheManager.newInstance("src/test/resources/ehcache/ehcache.xml"); // 獲得Cache的引用 Cache cache = manager.getCache("userCache"); // 將一個Element新增到Cache cache.put(new Element("key1", "value1")); // 獲取Element,Element類支援序列化,所以下面兩種方法都可以用 Element element1 = cache.get("key1"); // 獲取非序列化的值 log.debug("key:{}, value:{}", element1.getObjectKey(), element1.getObjectValue()); // 獲取序列化的值 log.debug("key:{}, value:{}", element1.getKey(), element1.getValue()); // 更新Cache中的Element cache.put(new Element("key1", "value2")); Element element2 = cache.get("key1"); log.debug("key:{}, value:{}", element2.getObjectKey(), element2.getObjectValue()); // 獲取Cache的元素數 log.debug("cache size:{}", cache.getSize()); // 獲取MemoryStore的元素數 log.debug("MemoryStoreSize:{}", cache.getMemoryStoreSize()); // 獲取DiskStore的元素數 log.debug("DiskStoreSize:{}", cache.getDiskStoreSize()); // 移除Element cache.remove("key1"); log.debug("cache size:{}", cache.getSize()); // 關閉當前CacheManager物件 manager.shutdown(); // 關閉CacheManager單例例項 CacheManager.getInstance().shutdown(); } }
快取配置
Ehcache支援通過xml檔案和API兩種方式進行配置。
xml方式
Ehcache的CacheManager
建構函式或工廠方法被呼叫時,會預設載入classpath下名為ehcache.xml的配置檔案。如果載入失敗,會載入Ehcache jar包中的ehcache-failsafe.xml檔案,這個檔案中含有簡單的預設配置。
ehcache.xml配置引數說明:
- name:快取名稱。
- maxElementsInMemory:快取最大個數。
- eternal:快取中物件是否為永久的,如果是,超時設定將被忽略,物件從不過期。
- timeToIdleSeconds:置物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
- timeToLiveSeconds:快取資料的生存時間(TTL),也就是一個元素從構建到消亡的最大時間間隔值,這隻能在元素不是永久駐留時有效,如果該值是0就意味著元素可以停頓無窮長的時間。
- maxEntriesLocalDisk:當記憶體中物件數量達到maxElementsInMemory時,Ehcache將會物件寫到磁碟中。
- overflowToDisk:記憶體不足時,是否啟用磁碟快取。
- diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
- maxElementsOnDisk:硬碟最大快取個數。
- diskPersistent:是否在VM重啟時儲存硬碟的快取資料。預設值是false。
- diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
- memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
- clearOnFlush:記憶體數量最大時是否清除。
ehcache.xml的一個範例
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- 磁碟快取位置 --> <diskStore path="java.io.tmpdir/ehcache"/> <!-- 預設快取 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <cache name="userCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="3" timeToLiveSeconds="3" maxEntriesLocalDisk="10000000" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"/> </ehcache>
API方式
xml配置的引數也可以直接通過程式設計方式來動態的進行配置(dynamicConfig沒有設為false)。
Cache cache = manager.getCache("sampleCache"); CacheConfiguration config = cache.getCacheConfiguration(); config.setTimeToIdleSeconds(60); config.setTimeToLiveSeconds(120); config.setmaxEntriesLocalHeap(10000); config.setmaxEntriesLocalDisk(1000000);
也可以通過disableDynamicFeatures()
方式關閉動態配置開關。配置以後你將無法再以程式設計方式配置引數。
Cache cache = manager.getCache("sampleCache"); cache.disableDynamicFeatures();
Spring整合Ehcache
Spring3.1開始新增了對快取的支援。和事務功能的支援方式類似,快取抽象允許底層使用不同的快取解決方案來進行整合。
Spring4.1開始支援JSR-107註解。
注:我本人使用的Spring版本為4.1.4.RELEASE,目前Spring版本僅支援Ehcache2.5以上版本,但不支援Ehcache3。
繫結Ehcache
org.springframework.cache.ehcache.EhCacheManagerFactoryBean
這個類的作用是載入Ehcache配置檔案。
org.springframework.cache.ehcache.EhCacheCacheManager
這個類的作用是支援net.sf.ehcache.CacheManager。
spring-ehcache.xml的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd"> <description>ehcache快取配置管理檔案</description> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache/ehcache.xml"/> </bean> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcache"/> </bean> <!-- 啟用快取註解開關 --> <cache:annotation-driven cache-manager="cacheManager"/> </beans>
使用Spring的快取註解
開啟註解
Spring為快取功能提供了註解功能,但是你必須啟動註解。
你有兩個選擇:
(1) 在xml中宣告
像上一節spring-ehcache.xml中的做法一樣,使用<cache:annotation-driven/>
<cache:annotation-driven cache-manager="cacheManager"/>
(2) 使用標記註解
你也可以通過對一個類進行註解修飾的方式在這個類中使用快取註解。
範例如下:
@Configuration @EnableCaching public class AppConfig { }
註解基本使用方法
Spring對快取的支援類似於對事務的支援。
首先使用註解標記方法,相當於定義了切點,然後使用Aop技術在這個方法的呼叫前、呼叫後獲取方法的入參和返回值,進而實現了快取的邏輯。
下面三個註解都是方法級別:
@Cacheable
表明所修飾的方法是可以快取的:當第一次呼叫這個方法時,它的結果會被快取下來,在快取的有效時間內,以後訪問這個方法都直接返回快取結果,不再執行方法中的程式碼段。
這個註解可以用condition
屬性來設定條件,如果不滿足條件,就不使用快取能力,直接執行方法。
可以使用key
屬性來指定key的生成規則。
@CachePut
與@Cacheable
不同,@CachePut
不僅會快取方法的結果,還會執行方法的程式碼段。
它支援的屬性和用法都與@Cacheable
一致。
@CacheEvict
與@Cacheable
功能相反,@CacheEvict
表明所修飾的方法是用來刪除失效或無用的快取資料。
下面是@Cacheable
、@CacheEvict
和@CachePut
基本使用方法的一個集中展示:
@Service public class UserService { // @Cacheable可以設定多個快取,形式如:@Cacheable({"books", "isbns"}) @Cacheable({"users"}) public User findUser(User user) { return findUserInDB(user.getId()); } @Cacheable(value = "users", condition = "#user.getId() <= 2") public User findUserInLimit(User user) { return findUserInDB(user.getId()); } @CachePut(value = "users", key = "#user.getId()") public void updateUser(User user) { updateUserInDB(user); } @CacheEvict(value = "users") public void removeUser(User user) { removeUserInDB(user.getId()); } @CacheEvict(value = "users", allEntries = true) public void clear() { removeAllInDB(); } }
@Caching
如果需要使用同一個快取註解(@Cacheable
、@CacheEvict
或@CachePut
)多次修飾一個方法,就需要用到@Caching
。
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") }) public Book importBooks(String deposit, Date date)
@CacheConfig
與前面的快取註解不同,這是一個類級別的註解。
如果類的所有操作都是快取操作,你可以使用@CacheConfig
來指定類,省去一些配置。
@CacheConfig("books") public class BookRepositoryImpl implements BookRepository { @Cacheable public Book findBook(ISBN isbn) {...} }
參考
如果想參考我的完整程式碼示例,請點選這裡訪問我的github。
下面是我在寫作時參考的資料或文章。
相關文章
- 另一種快取,Spring Boot 整合 Ehcache快取Spring Boot
- Mybatis 整合 ehcache快取MyBatis快取
- SpringBoot2 整合Ehcache元件,輕量級快取管理Spring Boot元件快取
- Ehcache介紹及整合Spring實現快取記憶體Spring快取記憶體
- Spring Boot:簡單使用EhCache快取框架Spring Boot快取框架
- Java快取EhcacheJava快取
- Spring boot學習(八)Spring boot配置ehcache快取框架Spring Boot快取框架
- Spring Boot基礎教程:EhCache快取的使用Spring Boot快取
- 使用EHCACHE三步搞定SPRING BOOT 快取Spring Boot快取
- Spring Boot Oauth2快取UserDetails到EhcacheSpring BootOAuth快取AI
- 快取初見——EhCache快取
- EhCache快取使用教程快取
- Spring Cache與Ehcache 3整合Spring
- Spring AOP整合redis(註解方式) 實現快取統一管理SpringRedis快取
- Spring Boot 快速整合 Ehcache3Spring Boot
- Spring Boot 2.x基礎教程:EhCache快取的使用Spring Boot快取
- SpringBoot快取管理(二) 整合Redis快取實現Spring Boot快取Redis
- Spring Boot 2.x基礎教程:使用EhCache快取叢集Spring Boot快取
- SpringBoot中Shiro快取使用Redis、EhcacheSpring Boot快取Redis
- Mybatis快取詳解MyBatis快取
- springboot mybatis 專案框架原始碼 shiro 整合程式碼生成器 ehcache快取Spring BootMyBatis框架原始碼快取
- Spring 框架快取註解Spring框架快取
- Spring Cache快取註解Spring快取
- springboot整合redis2.x,使用spring註解進行快取Spring BootRedis快取
- Redis詳解(十二)------ 快取穿透、快取擊穿、快取雪崩Redis快取穿透
- Springboot應用快取實踐之:Ehcache加持Spring Boot快取
- 搞懂分散式技術14:Spring Boot使用註解整合Redis快取分散式Spring BootRedis快取
- spring配置redis註解快取SpringRedis快取
- 修改Ehcache快取中取到的值,快取中的值也被修改了快取
- spring-boot-route(十二)整合redis做為快取SpringbootRedis快取
- spring boot使用Jedis整合Redis實現快取(AOP)Spring BootRedis快取
- 詳解K8s 映象快取管理kube-fledgedK8S快取
- JuiceFS 快取預熱詳解UI快取
- 深入Nginx + PHP 快取詳解NginxPHP快取
- Spring 快取Spring快取
- Spring 快取註解@Cacheable的用法Spring快取
- spring boot2整合ES詳解Spring Boot
- 快取穿透詳解及解決方案快取穿透
- 如何在SPRING中同時管理本地快取和分散式快取? - techblogSpring快取分散式