Spring整合Ehcache管理緩存

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基本操作

ElementCacheCacheManager是Ehcache最重要的API。

  • Element:緩存的元素,它維護著一個鍵值對。

  • Cache:它是Ehcache的核心類,它有多個Element,并被CacheManager管理。它實現了對緩存的邏輯行為。

  • CacheManager:Cache的容器對象,并管理著Cache的生命周期。

    創建CacheManager

    下面的代碼列舉了創建CacheManager的五種方式。
    使用靜態方法create()會以默認配置來創建單例的CacheManager實例。
    newInstance()方法是一個工廠方法,以默認配置創建一個新的CacheManager實例。
    此外,newInstance()還有幾個重載函數,分別可以通過傳入StringURLInputStream參數來加載配置文件,然后創建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 Zhang Peng
 */
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) {...}
}

轉:http://www.cnblogs.com/jingmoxukong/p/5975994.html

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容