官方文档:http://www.ehcache.org/generated/2.9.0/html/ehc-all/
参考文档:
http://www.cnblogs.com/jingmoxukong/p/5975994.html#_label13
http://blog.csdn.net/u012106290/article/details/52154367
关于EHCache3.x版本的使用详见此篇博客:
https://my.oschina.net/u/2608182/blog/890239
Maven依赖:
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.3</version>
</dependency>
1.独立使用
在class根目录下加入配置文件ehcache1.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" />
<!-- 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());
}
输出结果如下(包括日志):
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文件。
在官方文档中已给出多种加载配置文件的方式的解释:
关于更多配置文件的解释:
对于配置文件:
<?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>
解释如下:
1、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里)。
2、maxElementsInMemory:在内存中缓存的element的最大数目。
3、maxElementsOnDisk:在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
4、eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
5、overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。
二、以下属性是可选的:
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。
四、缓存的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