初識EHCache緩存框架(2.x版本)

官方文檔:http://www.ehcache.org/generated/2.9.0/html/ehc-all/html

參考文檔:java

http://www.cnblogs.com/jingmoxukong/p/5975994.html#_label13spring

http://blog.csdn.net/u012106290/article/details/52154367數據庫

關於EHCache3.x版本的使用詳見此篇博客:緩存

http://www.javashuo.com/article/p-bceierkl-hz.htmldom

Maven依賴:jvm

<dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.3</version>
        </dependency>

1.獨立使用

在class根目錄下加入配置文件ehcache1.xml.ide

<?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" />

    <!-- helloworld1緩存 -->
    <cache name = "hello-world1"
           maxElementsInMemory = "1000"
           eternal = "false"
           timeToIdleSeconds = "5"
           timeToLiveSeconds = "5"
           overflowToDisk = "false"
           memoryStoreEvictionPolicy = "LRU" />
</ehcache>

Java代碼以下:測試

public static void main(String[] args) {
        // Create a cache manager
        final CacheManager cacheManager = CacheManager.newInstance(EHCacheTest.class
                .getResource("/ehcache1.xml"));
        // create the cache called "hello-world"
        final Cache cache = cacheManager.getCache("hello-world1");

        // 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());
    }

輸出結果以下(包括日誌):this

DEBUG 18:15:24,365 [main](ConfigurationFactory.java:98) - Configuring ehcache from URL: file:/D:/WORKSPACE/intelljIdea/SSMProjectMaven/target/test-classes/ehcache1.xml
DEBUG 18:15:24,371 [main](ConfigurationFactory.java:150) - Configuring ehcache from InputStream
DEBUG 18:15:24,466 [main](BeanHandler.java:271) - Ignoring ehcache attribute xmlns:xsi
DEBUG 18:15:24,466 [main](BeanHandler.java:271) - Ignoring ehcache attribute xsi:noNamespaceSchemaLocation
DEBUG 18:15:24,474 [main](DiskStoreConfiguration.java:141) - Disk Store Path: C:\Users\FlyingHe\AppData\Local\Temp\/ehcache
DEBUG 18:15:24,526 [main](CacheManager.java:1102) - Creating new CacheManager with config URL: file:/D:/WORKSPACE/intelljIdea/SSMProjectMaven/target/test-classes/ehcache1.xml
DEBUG 18:15:24,531 [main](PropertyUtil.java:87) - propertiesString is null.
DEBUG 18:15:24,553 [main](ConfigurationHelper.java:189) - No CacheManagerEventListenerFactory class specified. Skipping...
DEBUG 18:15:25,168 [main](Cache.java:1044) - No BootstrapCacheLoaderFactory class specified. Skipping...
DEBUG 18:15:25,169 [main](Cache.java:1017) - CacheWriter factory not configured. Skipping...
DEBUG 18:15:25,169 [main](ConfigurationHelper.java:100) - No CacheExceptionHandlerFactory class specified. Skipping...
DEBUG 18:15:25,189 [main](Cache.java:1044) - No BootstrapCacheLoaderFactory class specified. Skipping...
DEBUG 18:15:25,189 [main](Cache.java:1017) - CacheWriter factory not configured. Skipping...
DEBUG 18:15:25,190 [main](ConfigurationHelper.java:100) - No CacheExceptionHandlerFactory class specified. Skipping...
DEBUG 18:15:25,243 [main](MemoryStore.java:180) - Initialized net.sf.ehcache.store.MemoryStore for hello-world1
DEBUG 18:15:25,406 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: LOCAL_OFFHEAP_SIZE
DEBUG 18:15:25,414 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: LOCAL_OFFHEAP_SIZE_BYTES
DEBUG 18:15:25,415 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: LOCAL_DISK_SIZE
DEBUG 18:15:25,416 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: LOCAL_DISK_SIZE_BYTES
DEBUG 18:15:25,417 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: WRITER_QUEUE_LENGTH
DEBUG 18:15:25,419 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: REMOTE_SIZE
DEBUG 18:15:25,419 [main](ExtendedStatisticsImpl.java:224) - Mocking Pass-Through Statistic: LAST_REJOIN_TIMESTAMP
DEBUG 18:15:25,516 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: OFFHEAP_GET
DEBUG 18:15:25,518 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: OFFHEAP_PUT
DEBUG 18:15:25,519 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: OFFHEAP_REMOVE
DEBUG 18:15:25,522 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: DISK_GET
DEBUG 18:15:25,523 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: DISK_PUT
DEBUG 18:15:25,524 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: DISK_REMOVE
DEBUG 18:15:25,525 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: XA_COMMIT
DEBUG 18:15:25,526 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: XA_ROLLBACK
DEBUG 18:15:25,556 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: XA_RECOVERY
DEBUG 18:15:25,565 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: CLUSTER_EVENT
DEBUG 18:15:25,566 [main](ExtendedStatisticsImpl.java:206) - Mocking Operation Statistic: NONSTOP
DEBUG 18:15:25,578 [main](Cache.java:1262) - Initialised cache: hello-world1
DEBUG 18:15:25,578 [main](ConfigurationHelper.java:334) - CacheDecoratorFactory not configured. Skipping for 'hello-world1'.
DEBUG 18:15:25,578 [main](ConfigurationHelper.java:364) - CacheDecoratorFactory not configured for defaultCache. Skipping for 'hello-world1'.
Hello, World!

注:

若是在實例化CacheManager時採用此種方式CacheManager manager = CacheManager.newInstance();則默認加載class根目錄下的ehcache.xml文件。

在官方文檔中已給出多種加載配置文件的方式的解釋:

地址:http://www.ehcache.org/generated/2.9.0/html/ehc-all/#page/Ehcache_Documentation_Set%2Fco-codebasics_loading_a_configuration.html%23

關於更多配置文件的解釋:

對於配置文件:

<?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"/>    
  <defaultCache    
    maxElementsInMemory="10000"    
    maxElementsOnDisk="0"    
    eternal="true"    
    overflowToDisk="true"    
    diskPersistent="false"    
    timeToIdleSeconds="0"    
    timeToLiveSeconds="0"    
    diskSpoolBufferSizeMB="50"    
    diskExpiryThreadIntervalSeconds="120"    
    memoryStoreEvictionPolicy="LFU"    
    />    
  <cache name="myCache"    
    maxElementsInMemory="100"    
    maxElementsOnDisk="0"    
    eternal="false"    
    overflowToDisk="false"    
    diskPersistent="false"    
    timeToIdleSeconds="120"    
    timeToLiveSeconds="120"    
    diskSpoolBufferSizeMB="50"    
    diskExpiryThreadIntervalSeconds="120"    
    memoryStoreEvictionPolicy="FIFO"    
    />    
</ehcache>

解釋以下:

一、diskStore :指定數據(.data and .index)存儲位置,可指定磁盤中的文件夾位置期 The diskStore element is optional. It must be configured if you have overflowToDisk or diskPersistent enabled    for any cache. If it is not configured, a warning will be issues and java.io.tmpdir will be used.
2、defaultCache : 默認的管理策略
1、如下屬性是必須的:
  一、name: Cache的名稱,必須是惟一的(ehcache會把這個cache放到HashMap裏)。
  二、maxElementsInMemory:在內存中緩存的element的最大數目。 
  三、maxElementsOnDisk:在磁盤上緩存的element的最大數目,默認值爲0,表示不限制。 
  4、eternal:設定緩存的elements是否永遠不過時。若是爲true,則緩存的數據始終有效,若是爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷。 
  5、overflowToDisk: 若是內存中數據超過內存限制,是否要緩存到磁盤上。 
2、如下屬性是可選的:
  1、timeToIdleSeconds: 對象空閒時間,指對象在多長時間沒有被訪問就會失效。只對eternal爲false的有效。默認值0,表示一直能夠訪問。
  2、timeToLiveSeconds: 對象存活時間,指對象從建立到失效所須要的時間。只對eternal爲false的有效。默認值0,表示一直能夠訪問。
  3、diskPersistent: 是否在磁盤上持久化。指重啓jvm後,數據是否有效。默認爲false。 
  4、diskExpiryThreadIntervalSeconds: 對象檢測線程運行時間間隔。標識對象狀態的線程多長時間運行一次。
  5、diskSpoolBufferSizeMB: DiskStore使用的磁盤大小,默認值30MB。每一個cache使用各自的DiskStore。 
  6、memoryStoreEvictionPolicy: 若是內存中數據超過內存限制,向磁盤緩存時的策略。默認值LRU,可選FIFO、LFU。 
4、緩存的3 種清空策略 :
  1、FIFO ,first in first out (先進先出).
  2、LFU , Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存。
  3、LRU ,Least Recently Used(最近最少使用). (ehcache 默認值).緩存的元素有一個時間戳,當緩存容量滿了,而又須要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。

2.EHCache2.x版本與Spring的整合使用

引入Spring相關的包這裏再也不詳述。

①建立一個ehcache-spring.xml的文件,配置Spring

<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.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!--開啓Spring的緩存註解,以便使用Cacheable,CachePut,CacheEvict等註解-->
    <cache:annotation-driven />
    <bean id = "personService" class = "at.flying.service.PersonService" />
    <!--
        用於讀取EHCache配置文件,注意這裏暴露的bean並非
        org.springframework.cache.ehcache.EhCacheManagerFactoryBean
        這個類的實例,而是net.sf.ehcache.CacheManager的實例
    -->
    <bean id = "ehcacheFactoryBean" class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name = "configLocation" value = "classpath:ehcache1.xml" />
    </bean>
    <!--使用EHCache配置文件建立CacheManager,拿到了CacheManager不就想幹嗎幹嗎了嘛^-^ -->
    <bean id = "cacheManager" class = "org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name = "cacheManager" ref = "ehcacheFactoryBean" />
    </bean>
</beans>

②建立ehcache1.xml文件,配置EHCache

<?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" />

    <!-- Person緩存 -->
    <cache name = "Person"
           maxElementsInMemory = "1000"
           eternal = "false"
           timeToIdleSeconds = "3"
           timeToLiveSeconds = "3"
           overflowToDisk = "false"
           memoryStoreEvictionPolicy = "LRU" />
</ehcache>

③建立一個Person類,須要緩存的對象的所屬類必須實現java.io.Serializable接口

package at.flying.domain;

import java.io.Serializable;


public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private int id;
    private String name;

    public Person() {}

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "id=" + this.id + ", name=" + this.name;
    }
}

④建立一個PersonService類,用於模擬對數據庫操做的業務邏輯

package at.flying.service;

import at.flying.domain.Person;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

public class PersonService {
    private Set<Person> people;

    public PersonService() {
        people = new HashSet<>();
        Person person1 = new Person(1, "張三");
        Person person2 = new Person(2, "趙四");
        Person person3 = new Person(3, "王五");
        people.add(person1);
        people.add(person2);
        people.add(person3);
    }

    @Cacheable({"Person"})
    public Person findUser(Person person) {
        return findUserInDB(person.getId());
    }

    @Cacheable(value = "Person", condition = "#person.getId() <= 2")
    public Person findUserInLimit(Person person) {
        return findUserInDB(person.getId());
    }

    @CachePut(value = "Person")
    public Person updateUser(Person person) {
        updateUserInDB(person);
        return person;
    }

    @CacheEvict(value = "Person")
    public void removeUser(Person person) {
        removeUserInDB(person.getId());
    }

    @CacheEvict(value = "Person", allEntries = true)
    public void clear() {
        removeAllInDB();
    }

    /**
     * 模擬查找數據庫
     */
    private Person findUserInDB(int id) {
        for (Person u : people) {
            if (id == u.getId()) {
                System.out.println("查找數據庫 id = " + id + " 成功");
                return u;
            }
        }
        return null;
    }

    /**
     * 模擬更新數據庫
     */
    private void updateUserInDB(Person person) {
        for (Person u : people) {
            if (person.getId() == u.getId()) {
                System.out.println("更新數據庫" + u + " -> " + person);
                u.setName(person.getName());
            }
        }
    }

    private void removeUserInDB(int id) {
        for (Person u : people) {
            if (id == u.getId()) {
                System.out.println("從數據庫移除 id = " + id + " 的數據");
                people.remove(u);
                break;
            }
        }
    }

    private void removeAllInDB() {
        people.clear();
    }
}

⑤測試類EHCacheSpringTest

package at.flying.test;

import at.flying.domain.Person;
import at.flying.service.PersonService;
import net.sf.ehcache.Element;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:at/flying/ehcache/spring/xml/ehcache-spring.xml"})
public class EHCacheSpringTest {
    @Autowired
    PersonService personService;
    @Autowired
    EhCacheCacheManager cacheManager;

    /**
     * 測試@Cacheable
     */
    @Test
    public void testFindUser() throws InterruptedException {
        // 設置查詢條件
        Person person1 = new Person(1, null);
        Person person2 = new Person(2, null);
        Person person3 = new Person(3, null);

        System.out.println("第一次查詢");
        System.out.println(personService.findUser(person1));
        System.out.println(personService.findUser(person2));
        System.out.println(personService.findUser(person3));
        this.printCacheStatus();

        System.out.println("\n第二次查詢");
        System.out.println(personService.findUser(person1));
        System.out.println(personService.findUser(person2));
        System.out.println(personService.findUser(person3));

        // 在classpath:ehcache/ehcache.xml中,設置了userCache的緩存時間爲3000 ms, 這裏設置等待
        Thread.sleep(3000);

        System.out.println("\n緩存過時,再次查詢");
        System.out.println(personService.findUser(person1));
        System.out.println(personService.findUser(person2));
        System.out.println(personService.findUser(person3));
    }

    /**
     * 輸出當前Person緩存的緩存信息
     */
    public void printCacheStatus() {
        net.sf.ehcache.CacheManager cacheManager = this.cacheManager.getCacheManager();
        net.sf.ehcache.Cache cache = cacheManager.getCache("Person");
        System.out.println("緩存對象數:" + cache.getSize());
        System.out.println("各元素的Key-Value以下:");
        List keys = cache.getKeys();
        for (Object o : keys) {
            Element e = cache.get(o);
            System.out.println(o + " -> " + e.getObjectValue());
        }
    }

    /**
     * 測試@Cacheable設置Spring SpEL條件限制
     */
    @Test
    public void testFindUserInLimit() throws InterruptedException {
        // 設置查詢條件
        Person person1 = new Person(1, null);
        Person person2 = new Person(2, null);
        Person person3 = new Person(3, null);

        System.out.println("第一次查詢user info");
        System.out.println(personService.findUserInLimit(person1));
        System.out.println(personService.findUserInLimit(person2));
        System.out.println(personService.findUserInLimit(person3));

        System.out.println("\n第二次查詢user info");
        System.out.println(personService.findUserInLimit(person1));
        System.out.println(personService.findUserInLimit(person2));
        System.out.println(personService.findUserInLimit(person3)); // 超過限制條件,不會從緩存中讀數據

        // 在classpath:ehcache/ehcache.xml中,設置了userCache的緩存時間爲3000 ms, 這裏設置等待
        Thread.sleep(3000);

        System.out.println("\n緩存過時,再次查詢");
        System.out.println(personService.findUserInLimit(person1));
        System.out.println(personService.findUserInLimit(person2));
        System.out.println(personService.findUserInLimit(person3));
    }

    /**
     * 測試@CachePut
     */
    @Test
    public void testUpdateUser() {
        // 設置查詢條件
        Person person2 = new Person(2, null);

        System.out.println(personService.findUser(person2));
        this.printCacheStatus();
        personService.updateUser(new Person(2, "尼古拉斯.趙四"));
        this.printCacheStatus();
        System.out.println(personService.findUser(person2));
    }

    /**
     * 測試@CacheEvict刪除指定緩存
     */
    @Test
    public void testRemoveUser() {
        // 設置查詢條件
        Person person1 = new Person(1, null);

        System.out.println("數據刪除前:");
        System.out.println(personService.findUser(person1));

        personService.removeUser(person1);
        System.out.println("數據刪除後:");
        System.out.println(personService.findUser(person1));
    }

    /**
     * 測試@CacheEvict刪除全部緩存
     */
    @Test
    public void testClear() {
        System.out.println("數據清空前:");
        System.out.println(personService.findUser(new Person(1, null)));
        System.out.println(personService.findUser(new Person(2, null)));
        System.out.println(personService.findUser(new Person(3, null)));

        personService.clear();
        System.out.println("\n數據清空後:");
        System.out.println(personService.findUser(new Person(1, null)));
        System.out.println(personService.findUser(new Person(2, null)));
        System.out.println(personService.findUser(new Person(3, null)));
    }
}

測試結果再也不貼出。

注意:

註解Cacheable與Cacheput的區別:

cacheable通常用於查詢數據,cacheput通常用於修改數據。二者都會對修飾的方法的返回值作緩存,若是在註解中沒有指定緩存時的key而且也沒有指定keyGenerator參數的話,那麼默認key將會是直接調用緩存對象的toString()方法的返回值做爲key,若是被修飾的方法沒有返回值,那麼緩存值value將會是null

而且對於Cacheable若是在查詢時發現緩存中存在,則再也不執行被修飾的方法體,也就是說不會取數據庫中查詢數據,而對於CachePut的話,無論怎樣被修飾的方法體都會執行,因此說,Cacheable通常用於查詢數據,而CachePut通常用於修改數據。

結果可測試獲得,這裏再也不貼出測試結果。

關於這三個註解的詳細解釋能夠參考此篇博客:

http://blog.csdn.net/u012106290/article/details/52154367

關於EHCache配置文件中diskStore的解釋可參看此篇博客:

http://www.cnblogs.com/crazylqy/p/4238265.html

相關文章
相關標籤/搜索