1. 程式人生 > >Ehcache快取框架詳解

Ehcache快取框架詳解

一、前言

ehcache是一個比較成熟的java快取框架,它提供了用記憶體,磁碟檔案儲存,以及分散式儲存方式等多種靈活的cache管理方案。ehcache最早從hibernate發展而來。由於3.x的版本和2.x的版本API差異比較大。這裡直接學習最新版本的了,但是最後整合spring的時候還是有2.x。

二、安裝

由於我的專案是使用maven管理的,因此只要在pom檔案中新增如下程式碼即可。

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId
>
<version>1.0.0</version> </dependency> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.3.1</version> </dependency>

好像ehcache還要依賴上面的那個Cache,因此最好兩個多加上。

三、使用

1、快速入門__JAVA實現

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder
() .withCache("preConfigured", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) .build(); cacheManager.init(); Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class
); Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))); myCache.put(1L, "da one!"); String value = myCache.get(1L); cacheManager.removeCache("preConfigured"); cacheManager.close();

這裡使用的官網的例子。

  • CacheManagerBuilder.newCacheManagerBuilder():建立快取構造器的構建者,類似工廠模式,注意,裡面的實現不是單例。
  • .withCache() :相當於建立一個自帶快取的CacheManager。
  • build() :這個方法就是建立CacheManager了。

    上面的build方法接受一個boolean引數:當為true時,CacheManager在使用時就不用初始化,否則,就需要CacheManager呼叫init()進行初始化操作。預設為false。
    
  • cacheManager.getCache():獲取快取

  • cacheManager.createCache():建立快取。
  • myCache.put(1L, “da one!”):在快取中新增值

2、快速入門__XML實現

    <cache alias="foo">
        <!-- 快取鍵值對的型別 -->
        <key-type>java.lang.String</key-type>
        <value-type>java.lang.String</value-type>
        <!-- 配置快取 -->
        <expiry>
            <ttl unit="seconds">2</ttl>
        </expiry>
        <resources>
            <!-- 在堆中申請2000個entries -->
            <heap unit="entries">2000</heap>
            <!-- 最大非堆記憶體 -->
            <offheap unit="MB">500</offheap>
        </resources>

    </cache>
    <!-- 定一個模板 -->
    <cache-template name="myDefaults">
        <key-type>java.lang.Long</key-type>
        <value-type>java.lang.String</value-type>
        <heap unit="entries">200</heap>
    </cache-template>
    <!-- 使用上面的模板 -->
    <cache alias="bar" uses-template="myDefaults">
        <key-type>java.lang.Number</key-type>
    </cache>

    <cache alias="simpleCache" uses-template="myDefaults" />
// 測試xml
    @Test
    public void test03() {
        URL url = getClass().getResource("/ehcache.xml");
        XmlConfiguration conf = new XmlConfiguration(url);
        CacheManager cacheManager = CacheManagerBuilder.newCacheManager(conf);
        cacheManager.init();
        Cache<String, String> cache = cacheManager.getCache("foo", String.class, String.class);
        cache.put("key", "value");
        try {
            Thread.sleep(1000); // 測試過期時間
            String value = cache.get("key");
            System.out.println("result:" + value);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

上面即演示了xml實現快取,註釋都比較清楚了。之所以在java中呼叫Thread.sleep(1000),就是為了測試在xml中設定過期時間是否有效。

三、磁碟快取

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .with(CacheManagerBuilder.persistence(new File("myData")))
                .withCache("threeTieredCache",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
                                ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)
                                        .offheap(1, MemoryUnit.MB).disk(20, MemoryUnit.MB, true)))
                .build(true);

        Cache<Long, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache", Long.class,
                String.class);
        threeTieredCache.put(1L, "stillAvailableAfterRestart");

        persistentCacheManager.close();

上面程式碼即會在當前專案的同級目錄下建立一個mydata目錄。

四、Spring和Ehcache整合

Spring本身並沒有提供快取的功能,但是卻對市面上好多快取框架提供了支援,即也支援Ehcache。由於Spring4好像對ehcache3.x不是太支援,因此這裡選用2.x。

ehcache.xml檔案:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!-- 指定一個檔案目錄,當EhCache把資料寫到硬碟上時,將把資料寫到這個檔案目錄下 -->
    <diskStore path="java.io.tmpdir" />

    <!-- 設定快取的預設資料過期策略 -->
    <defaultCache maxElementsInMemory="10000" eternal="false"
        overflowToDisk="true" timeToIdleSeconds="10" timeToLiveSeconds="20"
        diskPersistent="false" diskExpiryThreadIntervalSeconds="120" />
    <!-- 
        name:快取名稱

        maxElementsInMemory:記憶體中最大快取物件數

        maxElementsOnDisk:硬碟中最大快取物件數,若是0表示無窮大

        eternal:true表示物件永不過期,此時會忽略timeToIdleSeconds和timeToLiveSeconds屬性,預設為false

        overflowToDisk:true表示當記憶體快取的物件數目達到了

        maxElementsInMemory界限後,會把溢位的物件寫到硬碟快取中。注意:如果快取的物件要寫入到硬碟中的話,則該物件必須實現了Serializable接口才行。

        diskSpoolBufferSizeMB:磁碟快取區大小,預設為30MB。每個Cache都應該有自己的一個快取區。

        diskPersistent:是否快取虛擬機器重啟期資料,是否持久化磁碟快取,當這個屬性的值為true時,系統在初始化時會在磁碟中查詢檔名 為cache名稱,字尾名為index的檔案,這個檔案中存放了已經持久化在磁碟中的cache的index,找到後會把cache載入到記憶體,要想把 cache真正持久化到磁碟,寫程式時注意執行net.sf.ehcache.Cache.put(Element element)後要呼叫flush()方法。

        diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設為120秒

        timeToIdleSeconds: 設定允許物件處於空閒狀態的最長時間,以秒為單位。當物件自從最近一次被訪問後,如果處於空閒狀態的時間超過了timeToIdleSeconds屬性 值,這個物件就會過期,EHCache將把它從快取中清空。只有當eternal屬性為false,該屬性才有效。如果該屬性值為0,則表示物件可以無限 期地處於空閒狀態

        timeToLiveSeconds:設定物件允許存在於快取中的最長時間,以秒為單位。當物件自從被存放到快取中後,如果處於快取中的時間超過了 timeToLiveSeconds屬性值,這個物件就會過期,EHCache將把它從快取中清除。只有當eternal屬性為false,該屬性才有 效。如果該屬性值為0,則表示物件可以無限期地存在於快取中。timeToLiveSeconds必須大於timeToIdleSeconds屬性,才有 意義

        memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
     -->
    <cache name="cacheTest"
           maxElementsInMemory="1000"
            eternal="false"
        overflowToDisk="true" timeToIdleSeconds="10" timeToLiveSeconds="20" />

</ehcache>

application.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="  
           http://www.springframework.org/schema/beans  
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
           http://www.springframework.org/schema/aop  
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">

    <!-- 自動掃描註解的bean -->
    <context:component-scan base-package="com.lw.spring.ehcache" />

    <!-- 註解掃描 -->
    <cache:annotation-driven cache-manager="ehCacheCacheManager" />

    <!-- 例項CacheManager -->
    <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache"></property>
    </bean>

    <!-- 載入ehcache配置檔案 -->
    <bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"></property>
    </bean>

</beans>

建立一個service介面,並新增實現。

package com.lw.spring.ehcache;

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class EhcacheServiceImpl implements EhcacheService {

    /**
     * @Cacheable
     * 表明所修飾的方法是可以快取的:當第一次呼叫這個方法時,
     * 它的結果會被快取下來,在快取的有效時間內。如果每次快取返回的結果都是一樣的,則不會執行方法體的程式碼
     * 以後訪問這個方法都直接返回快取結果,不再執行方法中的程式碼段。
     * @CachePut:功能比@Cacheable更強,不僅快取方法結果,還快取程式碼段,每次都會執行方法體的內容
     * @CacheEvict:功能和@Cacheable功能相反,@CacheEvict表明所修飾的方法是用來刪除失效或無用的快取資料。
     * @CacheConfig:與前面的快取註解不同,這是一個類級別的註解。如果類的所有操作都是快取操作,你可以使用@CacheConfig來指定類,省去一些配置。
     * condition:快取的條件,可以為null,使用spel編寫,只有返回true的情況下才進行快取
     * 這個方法會以key中的值作為快取中的key,返回結果作為快取中的值進行快取
     */
    @CachePut(value = "cacheTest", key = "#param",condition="1<2")
    public String getTime(String param) {
        System.out.println("getTime方法呼叫了...");
        Long time = System.currentTimeMillis();
        return time.toString();
    }
}

測試:

package com.lw.spring.ehcache;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations={"classpath:application.xml"})
public class EhcacheTest  extends AbstractJUnit4SpringContextTests{

    @Autowired  
    private EhcacheService ehcacheService;

    @Test  
    public void getTimestampTest() throws InterruptedException{  
        System.out.println("第一次呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));

        System.out.println("呼叫:" + ehcacheService.getTime("param"));
        Thread.sleep(11000);// 10s之後快取消失
        System.out.println("再過11秒之後呼叫:" + ehcacheService.getTime("param"));
    } 
}

上面就完成了Spring和ehcache的結合了。其實主要就是ehcache的核心類交給了Spring。上面還有幾個註解,註釋裡也解釋了。