简介:纯java实现的缓存组件(jar包),最主要的作用还是做应用内缓存(进程内),比如hibernate就默认使用ehcache做缓存。我们可以创建多个缓存(cache),缓存中以key-value的形式存储数据,数据结构较为单一。
存储位置:
heap:由GC管理的JVM堆
offheap:堆外内存(非JVM的堆)
disk:持久化到硬盘,cache.destroy()方法可以删除硬盘上的缓存
其他功能
Eviction Advisors
缓存策略,说白了就是控制哪些缓存数据可存储,哪些不可存储,比如可以只让key为偶数的存储,或者只有value为String类可以存储。我们可以定义一个实现了EvictionAdvisor接口的类来控制。
User Managed Caches
由用户自己管理的缓存,不由CacheManager管理,用户可以自己控制cache的生命周期等等。但是为了方便,一般都是由CacheManager来管理。
Transactions Support
支持事务,我觉得一般用不到,如果它仅仅是一个缓存,并且缓存的逻辑会包含在service方法里,而service方法由spring的事务来管理,那么没必要用到它。
Cache-through
包括read-through和write-through,把缓存作为SOR(system of record),我个人还没弄清楚作为系统级别的缓存数据有什么作用
ehcache与redis的区别
1.redis的数据结构比较丰富,有key-value、hash、set等;ehcache比较简单,只有key-value
2.ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。redis是通过socket访问到缓存服务,效率ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。
编程方式配置cache
下面是两个例子
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.junit.Test;
/**
* writer: holien
* Time: 2017-08-11 11:52
* Intent: 入门测试ehcache
*/
public class EhCacheTest {
@Test
public void testEhcache() {
// 创建缓存管理器和一个叫“preConfigured”的缓存实例
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class,
String.class, ResourcePoolsBuilder.heap(10)))
.build();
// 初始化缓存管理器
cacheManager.init();
Cache preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
preConfigured.put(1L, "zheng pens");
// 覆盖上一个值
preConfigured.put(1L, "pens");
System.out.println(preConfigured.get(1L));
System.out.println(preConfigured.containsKey(1L));
// 不存在的key-value返回null
System.out.println(preConfigured.get(2L));
// 不允许值为null,报空指针错误
// preConfigured.put(3L, null);
// 不在创建cacheManeger时创建缓存实例,单独创建
Cache cache2 = cacheManager.createCache("cache2",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Integer.class, String.class,
ResourcePoolsBuilder.heap(5))
// .withEvictionAdvisor(new CustomEvictionAdvisor())
.build());
cache2.put(100, "hello");
System.out.println(cache2.get(100));
}
}
这个例子中把缓存的数据存储到硬盘
import org.ehcache.PersistentUserManagedCache;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.builders.UserManagedCacheBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.core.spi.service.LocalPersistenceService;
import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration;
import org.ehcache.impl.config.persistence.UserManagedPersistenceContext;
import org.ehcache.impl.persistence.DefaultLocalPersistenceService;
import org.junit.Test;
import java.io.File;
/**
* writer: holien
* Time: 2017-08-11 22:40
* Intent: 使用ehcache的持久化功能
*/
public class PersistenceCacheTest {
@Test
public void testPersistenceCache() throws Exception {
LocalPersistenceService persistenceService = new DefaultLocalPersistenceService(
new DefaultPersistenceConfiguration(new File("E:\\")));
PersistentUserManagedCache cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.with(new UserManagedPersistenceContext("persistentCache", persistenceService))
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.disk(10L, MemoryUnit.MB, true))
.build(true);
// 把缓存只存进硬盘里,只要persistenceService相同,即使关闭,再次启动还是可以读取数据
cache.put(42L, "The Answer!");
System.out.println(cache.get(42L));
// cache.remove(44L);
// 手动关闭和销毁
cache.close();
// 删除硬盘上的缓存
// cache.destroy();
// 停止服务
persistenceService.stop();
}
}
创建CacheConfiguration时设置该缓存的entry的过期时间
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.SECONDS)))
官方实例的xml文件,各种属性作一个介绍
java.lang.Long
com.pany.domain.Product
2
com.pany.ehcache.OddEvictionAdvisor
com.pany.ehcache.integration.ProductCacheLoaderWriter
200
java.lang.Long
com.pany.domain.Customer
200
使用我自己定义的ehcache.xml配置的cache
java.lang.Long
java.lang.String
1000
2
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.xml.XmlConfiguration;
import org.junit.Test;
import java.net.URL;
/**
* writer: holien
* Time: 2017-08-13 14:24
* Intent: 使用xml配置cache
*/
public class XmlConfugureTest {
@Test
public void testXmlConfiguration() throws Exception {
// URL location = new URL("file:E:\\IdeaProjects\\ehCacheTest\\web\\ehcache.xml");
URL location = getClass().getResource("/ehcache.xml");
System.out.println(location.toString());
XmlConfiguration xmlConfiguration = new XmlConfiguration(location);
CacheManager cacheManager = CacheManagerBuilder.newCacheManager(xmlConfiguration);
cacheManager.init();
Cache cache = cacheManager.getCache("test", Long.class, String.class);
cache.put(1L, "111");
cache.put(2L, "222");
System.out.println(cache.get(1L));
System.out.println(cache.get(2L));
}
}
当然,最好还是由spring管理cacheManager,然后使用xml来配置cache的属性,这样管理起来比较方便,在另一篇文章,讲讲ehcache2与spring的整合以及注解。
cache运行期间添加监听器ListenerObject listener = new ListenerObject(); 这个ListenerObject类就是我们自己定义的继承了CacheEventAdapter抽象类的类。
cache.getRuntimeConfiguration().registerCacheEventListener(listener, EventOrdering.ORDERED, EventFiring.ASYNCHRONOUS, EnumSet.of(EventType.CREATED, EventType.REMOVED));
cache运行期间解除监听器
cache.getRuntimeConfiguration().deregisterCacheEventListener(listener);
官方3.1v文档说提供了CacheEventAdapter抽象类方便我们重写监听器的方法,但是CacheEventAdapter 里的方法却是protected的,只能通过下载源码,然后改写源码重新打jar包,比较麻烦,后期修改也麻烦,也许是一个bug。
总结
ehcache也有集群的功能,但是我觉得ehcache还是适合一些简单的应用缓存,比如方法级别的,缓存方法的返回值。或者当作一个Map来存储不由GC管理的、可以持久化的数据,比如爬虫url的存储。