简单介绍
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。Ehcache是一种广泛使用的开 源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
-
优点:
- 快速
- 简单
- 多种缓存策略
- 缓存数据有两级:内存和磁盘,因此无需担心容量问题
- 缓存数据会在虚拟机重启的过程中写入磁盘
- 可以通过RMI、可插入API等方式进行分布式缓存
- 具有缓存和缓存管理器的侦听接口
- 支持多缓存管理器实例,以及一个实例的多个缓存区域
- 提供Hibernate的缓存实现
-
缺点:
- 使用磁盘Cache的时候非常占用磁盘空间:这是因为DiskCache的算法简单,该算法简单也导致Cache的效率非常高。它只是对元素直接追加存储。因此搜索元素的时候非常的快。如果使用DiskCache的,在很频繁的应用中,很快磁盘会满。
- 不能保证数据的安全:当突然kill掉java的时候,可能会产生冲突,EhCache的解决方法是如果文件冲突了,则重建cache。这对于Cache 数据需要保存的时候可能不利。当然,Cache只是简单的加速,而不能保证数据的安全。如果想保证数据的存储安全,可以使用Bekeley DB Java Edition版本。这是个嵌入式数据库。可以确保存储安全和空间的利用率。
使用Spring的AOP进行整合,可以灵活的对方法的返回结果对象进行缓存。
添加ehcache配置文件ehcache-setting.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- updateCheck:是否检查当前使用的Ehcache的版本 -->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<!-- 缓存到磁盘路径 -->
<diskStore path="d:/cache" />
<!--
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期
maxElementsInMemory:缓存中允许创建的最大对象数
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。
FIFO,先进先出
LFU,最少被使用,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<!-- 默认缓存 -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
maxElementsOnDisk="10000000" diskPersistent="false"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
<!-- 自定义缓存 -->
<cache name="baseCache" maxElementsInMemory="200"
maxElementsOnDisk="1000" eternal="false" overflowToDisk="true"
diskSpoolBufferSizeMB="20" timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
ehcache.xml
cache元素的属性
name:缓存名称
maxElementsInMemory:内存中最大缓存对象数
maxElementsOnDisk:硬盘中最大缓存对象数,若是0表示无穷大
eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false
overflowToDisk:true表示当内存缓存的对象数目达到了
maxElementsInMemory 界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。
diskSpoolBufferSizeMB:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。
diskPersistent:是否缓存虚拟机重启期数据,是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名 为cache名称,后缀名为index的文件,这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存,要想把 cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认为120秒
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性 值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限 期地处于空闲状态
timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有 效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有 意义
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
spring配置文件application.xml
<cache:annotation-driven cache-manager="cacheManager" />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache"></property>
</bean>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache-setting.xml"></property>
</bean>
程序中的实现
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.luo.service.EhCacheTestService;
@Service
public class EhCacheTestServiceImpl{
@Cacheable(value="baseCache",key="#param")
public String getTimestamp(String param) {
Long timestamp = System.currentTimeMillis();
return timestamp.toString();
}
}
这里注解中value=”cacheTest”与ehcache-setting.xml中的cache名称属性值一致。
@Cacheable
@Cacheable注解可以用在方法或者类级别,用在方法上,仅仅缓存此方法的返回值; 用在类上,针对该类所有的方法的返回值缓存。
当缓存中没有该对象的时候,当然要从数据库里面访问了,从数据库查出来之后,缓存管理器会将此对象放到缓存中,下一次访问的时候,只要该对象没有消亡则会从缓存里取,不会再查询数据库。
@Cacheable注解包含三个参数 @Cacheable(value,key,condition);
value:我们自定义缓存的name,将被缓存的位置;
key :
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 | 描述 | 示例 |
---|---|---|
methodName | 当前方法名 | #root.methodName |
method | 当前方法 | #root.method.name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的class | #root.targetClass |
args | 当前方法参数组成的数组 | #root.args[0] |
caches | 当前被调用的方法使用的Cache | #root.caches[0].name |
当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
condition属性指定发生的条件
有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
@CachePut
在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
returnnull;
}
@CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
allEntries属性
allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
beforeInvocation属性
清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
键的生成策略
键的生成策略有两种,一种是默认策略,一种是自定义策略。
默认策略
默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:
n 如果方法没有参数,则使用0作为key。
n 如果只有一个参数的话则使用该参数作为key。
n 如果参数多余一个的话则使用所有参数的hashCode作为key。
如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。
使用基于注解的配置时是通过cache:annotation-driven指定的.
<cache:annotation-driven key-generator="userKeyGenerator"/>
<bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>
import java.io.Serializable;
import com.googlecode.ehcache.annotations.key.AbstractCacheKeyGenerator;
public class UserKeyGenerator extends AbstractCacheKeyGenerator<Serializable>{
@Override
public Serializable generateKey(Object... data) {
if(data[0] instanceof com.xianxing.common.lang.Page){
com.xianxing.common.lang.Page page = (com.xianxing.common.lang.Page) data[0];
long clubId = (Long) data[1];
String result = page.getPage() + "-" + clubId;
return result.hashCode();
}
new IllegalArgumentException();
return null;
}
}
自定义策略
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”,上面已经写了。
echache 不使用spring管理用法
EHCacheConfig echache配置类
/**
* @program: platform
* @description:
* @author: lee
* @create: 2019-03-22
**/
public class EHCacheConfig {
/**
* 元素最大数量
*/
public static int MAXELEMENTSINMEMORY = 50000;
/**
* 是否把溢出数据持久化到硬盘
*/
public static boolean OVERFLOWTODISK = true;
/**
* 是否会死亡
*/
public static boolean ETERNAL = false;
/**
* 缓存的间歇时间
*/
public static int TIMETOIDLESECONDS = 600;
/**
* 存活时间(默认一天)
*/
public static int TIMETOlIVESECONDS = 86400;
/**
* 需要持久化到硬盘否
*/
public static boolean DISKPERSISTENT = false;
/**
* 内存存取策略
*/
public static String MEMORYSTOREEVICTIONPOLICY = "LFU";
}
echache工具类
/**
* @program: platform
* @description: EHCache工具类
* @author: lee
* @create: 2019-03-22
**/
import java.util.List;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import org.apache.commons.lang.StringUtils;
/**
* ehcache工具类
*/
public class EHCacheUtil {
private static CacheManager cacheManager = null;
private static Cache cache = null;
static {
EHCacheUtil.initCacheManager();
EHCacheUtil.initCache("cache");
}
/**
* 初始化缓存管理容器
*/
public static CacheManager initCacheManager() {
try {
if (cacheManager == null)
cacheManager = CacheManager.getInstance();
} catch (Exception e) {
e.printStackTrace();
}
return cacheManager;
}
/**
* 初始化缓存管理容器
*
* @param path ehcache.xml存放的路徑
*/
public static CacheManager initCacheManager(String path) {
try {
if (cacheManager == null) {
cacheManager = CacheManager.getInstance().create(path);
}
} catch (Exception e) {
e.printStackTrace();
}
return cacheManager;
}
/**
* 初始化cache
*/
public static Cache initCache(String cacheName) {
checkCacheManager();
if (null == cacheManager.getCache(cacheName)) {
cacheManager.addCache(cacheName);
}
cache = cacheManager.getCache(cacheName);
return cache;
}
/**
* 添加缓存
*
* @param key 关键字
* @param value 值
*/
public static void put(Object key, Object value) {
checkCache();
// 创建Element,然后放入Cache对象中
Element element = new Element(key, value);
cache.put(element);
}
/**
* 获取cache
*
* @param key 关键字
* @return
*/
public static Object get(Object key) {
checkCache();
Element element = cache.get(key);
if (null == element) {
return null;
}
return element.getObjectValue();
}
/**
* 初始化缓存
*
* @param cacheName 缓存名称
* @param maxElementsInMemory 元素最大数量
* @param overflowToDisk 是否持久化到硬盘
* @param eternal 是否会死亡
* @param timeToLiveSeconds 缓存存活时间
* @param timeToIdleSeconds 缓存的间隔时间
* @return 缓存
* @throws Exception
*/
public static Cache initCache(String cacheName, int maxElementsInMemory, boolean overflowToDisk, boolean eternal,
long timeToLiveSeconds, long timeToIdleSeconds) throws Exception {
try {
CacheManager singletonManager = CacheManager.create();
Cache myCache = singletonManager.getCache(cacheName);
if (myCache != null) {
CacheConfiguration config = cache.getCacheConfiguration();
config.setTimeToLiveSeconds(timeToLiveSeconds);
config.setMaxElementsInMemory(maxElementsInMemory);
config.setOverflowToDisk(overflowToDisk);
config.setEternal(eternal);
config.setTimeToIdleSeconds(timeToIdleSeconds);
}
if (myCache == null) {
Cache memoryOnlyCache = new Cache(cacheName, maxElementsInMemory, overflowToDisk, eternal, timeToLiveSeconds,
timeToIdleSeconds);
singletonManager.addCache(memoryOnlyCache);
myCache = singletonManager.getCache(cacheName);
}
return myCache;
} catch (Exception e) {
throw new Exception("init cache " + cacheName + " failed!!!");
}
}
/**
* 初始化cache
*
* @param cacheName cache的名字
* @param timeToLiveSeconds 有效时间
* @return cache 缓存
* @throws Exception
*/
public static Cache initCache(String cacheName, long timeToLiveSeconds) throws Exception {
return EHCacheUtil.initCache(cacheName, EHCacheConfig.MAXELEMENTSINMEMORY, EHCacheConfig.OVERFLOWTODISK,
EHCacheConfig.ETERNAL, timeToLiveSeconds, EHCacheConfig.TIMETOIDLESECONDS);
}
/**
* 初始化Cache
*
* @param cacheName cache容器名
* @return cache容器
* @throws Exception
*/
public static Cache initMyCache(String cacheName) throws Exception {
return EHCacheUtil.initCache(cacheName, EHCacheConfig.TIMETOlIVESECONDS);
}
/**
* 修改缓存容器配置
*
* @param cacheName 缓存名
* @param timeToLiveSeconds 有效时间
* @param maxElementsInMemory 最大数量
* @throws Exception
*/
public static boolean modifyCache(String cacheName, long timeToLiveSeconds, int maxElementsInMemory) throws Exception {
try {
if (StringUtils.isNotBlank(cacheName) && timeToLiveSeconds != 0L && maxElementsInMemory != 0) {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
CacheConfiguration config = myCache.getCacheConfiguration();
config.setTimeToLiveSeconds(timeToLiveSeconds);
config.setMaxElementsInMemory(maxElementsInMemory);
return true;
} else {
return false;
}
} catch (Exception e) {
throw new Exception("modify cache " + cacheName + " failed!!!");
}
}
/**
* 向指定容器中设置值
*
* @param vesselName 容器名
* @param key 键
* @param value 值
* @return 返回真
* @throws Exception 异常
*/
public static boolean setValue(String cacheName, String key, Object value) throws Exception {
try {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
if (myCache == null) {
myCache = initCache(cacheName);
}
myCache.put(new Element(key, value));
return true;
} catch (Exception e) {
throw new Exception("set cache " + cacheName + " failed!!!");
}
}
/**
* 向指定容器中设置值
*
* @param cacheName 容器名
* @param key 键
* @param value 值
* @param timeToIdleSeconds 间歇时间
* @param timeToLiveSeconds 存活时间
* @return 真
* @throws Exception 抛出异常
*/
public static boolean setValue(String cacheName, String key, Object value, Integer timeToLiveSeconds) throws Exception {
try {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
if (myCache == null) {
initCache(cacheName, timeToLiveSeconds);
myCache = myManager.getCache(cacheName);
}
myCache.put(new Element(key, value, EHCacheConfig.ETERNAL, EHCacheConfig.TIMETOIDLESECONDS, timeToLiveSeconds));
return true;
} catch (Exception e) {
throw new Exception("set cache " + cacheName + " failed!!!");
}
}
/**
* 从ehcache的指定容器中取值
*
* @param key 键
* @return 返回Object类型的值
* @throws Exception 异常
* @createTime 2012-4-23
*/
public static Object getValue(String cacheName, String key) throws Exception {
try {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
if (myCache == null) {
myCache = initMyCache(cacheName);
}
return myCache.get(key).getValue();
} catch (Exception e) {
return null;
}
}
/**
* 删除指定的ehcache容器
*
* @param vesselName
* @return 真
* @throws Exception 失败抛出异常
*/
public static boolean removeEhcache(String cacheName) throws Exception {
try {
CacheManager myManager = CacheManager.create();
myManager.removeCache(cacheName);
return true;
} catch (Exception e) {
throw new Exception("remove cache " + cacheName + " failed!!!");
}
}
/**
* 删除所有的EHCache容器
*
* @param cacheName 容器名
* @return 返回真
* @throws Exception 失败抛出异常
*/
public static boolean removeAllEhcache(String cacheName) throws Exception {
try {
CacheManager myManager = CacheManager.create();
myManager.removalAll();
return true;
} catch (Exception e) {
throw new Exception("remove cache " + cacheName + " failed!!!");
}
}
/**
* 删除EHCache容器中的元素
*
* @param cacheName 容器名
* @param key 键
* @return 真
* @throws Exception 失败抛出异常
*/
public static boolean removeElment(String cacheName, String key) throws Exception {
try {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
myCache.remove(key);
return true;
} catch (Exception e) {
throw new Exception("remove cache " + cacheName + " failed!!!");
}
}
/**
* 删除指定容器中的所有元素
*
* @param cacheName 容器名
* @param key 键
* @return 真
* @throws Exception 失败抛出异常
*/
public static boolean removeAllElment(String cacheName, String key) throws Exception {
try {
CacheManager myManager = CacheManager.create();
Cache myCache = myManager.getCache(cacheName);
myCache.removeAll();
return true;
} catch (Exception e) {
throw new Exception("remove cache " + cacheName + " failed!!!");
}
}
/**
* 释放CacheManage
*/
public static void shutdown() {
cacheManager.shutdown();
}
/**
* 移除cache
*
* @param cacheName
*/
public static void removeCache(String cacheName) {
checkCacheManager();
cache = cacheManager.getCache(cacheName);
if (null != cache) {
cacheManager.removeCache(cacheName);
}
}
/**
* 移除cache中的key
*
* @param cacheName
*/
public static void remove(String key) {
checkCache();
cache.remove(key);
}
/**
* 移除所有cache
*/
public static void removeAllCache() {
checkCacheManager();
cacheManager.removalAll();
}
/**
* 移除所有Element
*/
public static void removeAllKey() {
checkCache();
cache.removeAll();
}
/**
* 获取所有的cache名称
*
* @return
*/
public static String[] getAllCaches() {
checkCacheManager();
return cacheManager.getCacheNames();
}
/**
* 获取Cache所有的Keys
*
* @return
*/
public static List getKeys() {
checkCache();
return cache.getKeys();
}
/**
* 检测cacheManager
*/
private static void checkCacheManager() {
if (null == cacheManager) {
throw new IllegalArgumentException("调用前请先初始化CacheManager值:EHCacheUtil.initCacheManager");
}
}
private static void checkCache() {
if (null == cache) {
throw new IllegalArgumentException("调用前请先初始化Cache值:EHCacheUtil.initCache(参数)");
}
}
public static void main(String[] arg) {
// 初始化--必须
EHCacheUtil.initCacheManager();
EHCacheUtil.initCache("cache");
EHCacheUtil.put("A", "AAAAA");
EHCacheUtil.put("B", "BBBBB");
EHCacheUtil.put("F", "FFFFF");
System.out.println(EHCacheUtil.get("F"));
List keys = EHCacheUtil.getKeys();
for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
}
EHCacheUtil.shutdown();
}
}