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) {...}
}

參考

如果想參考我的完整代碼示例,請點擊這裏訪問我的github。

下面是我在寫作時參考的資料或文章。
Ehcache github
Ehcache官方文檔
Ehcache詳細解讀
註釋驅動的 Spring cache 緩存介紹
Spring官方文檔4.3.3.RELEASE 第36章緩存抽象

發佈了182 篇原創文章 · 獲贊 32 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章