您的位置:首页 > 编程语言 > Java开发

Spring集成ehcache

2016-03-10 16:38 399 查看
我的环境是:ehcache-2.10.1.jar,spring4.0.6。

1.首先要准备好Jar包

http://www.ehcache.org/下载,如果是maven的话:

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.1</version>
<type>pom</type>
</dependency>


2.接下来配置spring xml:

<!-- 配置ehcache -->
<!-- 开启ehcache注解 -->
<cache:annotation-driven cache-manager="cacheManager" />

<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory" />
</bean>

<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManagerFactory" />
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean>

<!-- 创建缓存、查询缓存的拦截器 -->
<bean id="cacheMethodInterceptor" class="net.kehwa.tb.board.core.ehcache.CacheMethodInterceptor">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>

<!-- 更新缓存、删除缓存的拦截器 -->
<bean id="cacheAfterReturningAdvice" class="net.kehwa.tb.board.core.ehcache.CacheAfterReturningAdvice">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!-- 插入拦截器,确认调用哪个拦截器,拦截器拦截的方法名特点等,此处调用拦截器CacheMethodInterceptor -->
<bean id="cachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 加入切面,切面为当执行完print方法后,在执行加入的切面 -->
<property name="advice">
<ref local="cacheMethodInterceptor" />
</property>
<property name="patterns">
<list>
<!--
###  .表示符合任何单一字元
###  +表示符合前一个字元一次或多次
###  *表示符合前一个字元零次或多次
###  \Escape任何Regular expression使用到的符号
-->
<!-- .*表示前面的前缀(包括包名),意思是表示getObject方法-->
<value>.*query.*</value>
<value>.*get.*</value>
<value>.*find.*</value>
</list>
</property>
</bean>

<!-- 插入拦截器,确认调用哪个拦截器,拦截器拦截的方法名特点等,此处调用拦截器CacheAfterReturningAdvice -->
<bean id="cachePointCutAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="cacheAfterReturningAdvice" />
</property>
<property name="patterns">
<list>
<!-- .*表示前面的前缀(包括包名),意思是updateObject方法-->
<value>.*delete.*</value>
<value>.*update.*</value>
<value>.*add.*</value>
<value>.*save.*</value>
<value>.*edit.*</value>
</list>
</property>
</bean>

<!-- 代理工厂 org.springframework.aop.framework.ProxyFactoryBean 只拦截一个 -->
<bean id="proxyFactory" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 说明调用接口bean名称 -->
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<!-- 说明拦截器bean名称 -->
<property name="interceptorNames">
<list>
<value>cachePointCut</value>
<value>cachePointCutAdvice</value>
</list>
</property>
</bean>


3.配置ehcache.xml,我在spring xml中设置了ehcache.xml的位置在src某目录下:

<?xml version="1.0" encoding="utf-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect">
<diskStore path="d:/cachetmpdir" /> <!--  java.io.tmpdir 默认临时文件路径    user.dir 用户当前工作目录    user.home 用户主目录 -->

<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />

<cache name="daoCache"
maxElementsInMemory="10000"
maxElementsOnDisk="1000"
eternal="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" />

<!--
name:缓存名称。通常为缓存对象的类名(非严格标准)。
maxElementsInMemory:设置基于内存的缓存可存放对象的最大数目。
maxElementsOnDisk:设置基于硬盘的缓存可存放对象的最大数目。
eternal:如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false;
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,
这个对象就会过期。当对象过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,
这个对象就会过期。当对象过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。
timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义。
overflowToDisk:如果为true,表示当基于内存的缓存中的对象数目达到了maxElementsInMemory界限后,会把益出的对象写到基于硬盘的缓存中。
注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。
memoryStoreEvictionPolicy:缓存对象清除策略。有三种:
1 FIFO ,first in first out ,这个是大家最熟的,先进先出,不多讲了
2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
2 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,
而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>


4.接下来是编写我们的拦截器了,注意我的拦截器的包和你拦截器所在的包不一样,请在spring xml里做修改。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

public class CacheMethodInterceptor implements MethodInterceptor, InitializingBean {

private Cache cache;

public void setCache(Cache cache) {
this.cache = cache;
}

public CacheMethodInterceptor() {
super();
}

/**
* 拦截ServiceManager的方法,并查找该结果是否存在,如果存在就返回cache中的值,
* 否则,返回数据库查询结果,并将查询结果放入cache
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取要拦截的类
String targetName = invocation.getThis().getClass().getName();
// 获取要拦截的类的方法
String methodName = invocation.getMethod().getName();
// 获得要拦截的类的方法的参数
Object[] arguments = invocation.getArguments();
Object result;

// 创建一个字符串,用来做cache中的key
String cacheKey = getCacheKey(targetName, methodName, arguments);
// 从cache中获取数据
Element element = cache.get(cacheKey);

if (element == null) {
// 如果cache中没有数据,则查找非缓存,例如数据库,并将查找到的放入cache
result = invocation.proceed();
// 生成将存入cache的key和value
element = new Element(cacheKey, result);
System.out.println(cacheKey + "-----进入非缓存中查找,例如直接查找数据库,查找后放入缓存");
// 将key和value存入cache
cache.put(element);
} else {
// 如果cache中有数据,则查找cache
System.out.println(cacheKey + "-----进入缓存中查找,不查找数据库,缓解了数据库的压力");
}
return element.getObjectValue();
}

/**
* 获得cache的key的方法,cache的key是Cache中一个Element的唯一标识,
* 包括包名+类名+方法名,如:com.test.service.TestServiceImpl.getObject
*/
private String getCacheKey(String targetName, String methodName, Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}

/**
* implement InitializingBean,检查cache是否为空 70
* 此方法会在所有属性都set之后调用
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}


// 接下来是清除缓存的
package net.kehwa.tb.board.core.ehcache;

import java.lang.reflect.Method;
import java.util.List;

import net.sf.ehcache.Cache;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

public class CacheAfterReturningAdvice implements AfterReturningAdvice, InitializingBean {

private Cache cache;

public void setCache(Cache cache) {
this.cache = cache;
}

public CacheAfterReturningAdvice() {
super();
}

public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
String className = arg3.getClass().getName();
List<?> list = cache.getKeys();
for (int i = 0; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if (cacheKey.startsWith(className)) {
cache.remove(cacheKey);
}
}
System.out.println("-----清除缓存");
}

public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}

}


对于某一些特别的,不符合我们命名规范的方法,我们不想写在拦截器里,但是却想缓存对它进行处理。那么我们需要对相应的实现类(不是接口)进行注解:

@Cacheable(value="daoCache")
public List<CyclicalLife> totalCyclicalLifeForm(String date) {
return cycDao.totalCyclicalLifeForm(date);
}


@Cacheable(value=”daoCache”)注解表示我们需要缓存它,一般的我们会设置一个key值来确保其在缓存中更好的使用。

@CacheEvict(value=”daoCache”,allEntries=true) 注解表示我们需要清理缓存,allEntries=true表示清理全部缓存。

注解名描述
CacheableAnnotation indicating that the result of invoking a method (or all methods in a class) can be cached.
CacheConfig@CacheConfig provides a mechanism for sharing common cache-related settings at the class level.
CacheEvictAnnotation indicating that a method (or all methods on a class) triggers a cache evict operation.
CachePutAnnotation indicating that a method (or all methods on a class) triggers a cache put operation.
CachingGroup annotation for multiple cache annotations (of different or the same type).
EnableCachingEnables Spring’s annotation-driven cache management capability, similar to the support found in Spring’s XML namespace.
详情请看spring注解方面的文档。

需要注意的是,如果没有配置keyGenerator,那么Cacheable的默认key是”“,这样的话就必须手工指定一个了。

<bean id="userKeyGenerator" class="net.kehwa.tb.board.core.ehcache.UserKeyGenerator" />
<cache:annotation-driven cache-manager="cacheManager" key-generator="userKeyGenerator"/>


userKeyGenerator是我们自己的类:

package net.kehwa.tb.board.core.ehcache;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;

public class UserKeyGenerator implements KeyGenerator {

@Override
public Object generate(Object target, Method method, Object... params) {
// 获取要拦截的类
String targetName = target.getClass().getName();
// 获取要拦截的类的方法
String methodName = method.getName();
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((params != null) && (params.length != 0)) {
for (int i = 0; i < params.length; i++) {
sb.append(".").append(params[i]);
}
}
return sb.toString();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: