首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

Ehcache学习(三)

2012-11-20 
Ehcache学习(3)5.?? 在 Spring中运用 EHCache需要使用 Spring 来实现一个 Cache 简单的解决方案,具体需求

Ehcache学习(3)
5.?? 在 Spring中运用 EHCache

需要使用 Spring 来实现一个 Cache 简单的解决方案,具体需求如下:使用任意一个现有开源 Cache Framework,要求使用 Cache 系统中 Service 或则 DAO 层的 get/find 等方法返回结果,如果数据更新(使用 Create/update/delete 方法),则刷新 cache 中相应的内容。根据需求,计划使用 Spring AOP + ehCache 来实现这个功能,采用 ehCache 原因之一是Spring 提供了 ehCache 的支持,至于为何仅仅支持 ehCache 而不支持 osCache 和JBossCache 无从得知(Hibernate???),但毕竟 Spring 提供了支持,可以减少一部分工作量:)。二是后来实现了 OSCache 和 JBoss Cache 的方式后,经过简单测试发现几个 Cache 在效率上没有太大的区别(不考虑集群),决定采用 ehCahce。AOP 嘛,少不了拦截器,先创建一个实现了 MethodInterceptor 接口的拦截器,用来拦截Service/DAO 的方法调用,拦截到方法后,搜索该方法的结果在 cache 中是否存在,如果存在,返回 cache 中的缓存结果,如果不存在,返回查询数据库的结果,并将结果缓存到 cache 中。

MethodCacheInterceptor.java

Java代码

package com.co.cache.ehcache;

import java.io.Serializable;

import net.sf.ehcache.Cache;

import net.sf.ehcache.Element;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.util.Assert;

public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean

{

private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);

private Cache cache;

public void setCache(Cache cache) {

this.cache = cache;

}

public MethodCacheInterceptor() {

super();

}

/**

*拦截 Service/DAO的方法,并查找该结果是否存在,如果存在就返回 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;

logger.debug("Find object from cache is " + cache.getName());

String cacheKey = getCacheKey(targetName, methodName, arguments);

Element element = cache.get(cacheKey);

if (element == null) {

logger.debug("Hold up method , Get method result and create cache........!");

result = invocation.proceed();

element = new Element(cacheKey, (Serializable) result);

cache.put(element);

}

return element.getValue();

}

/**

*获得 cache key的方法,cache key是 Cache中一个 Element的唯一标识

* cache key包括 包名+类名+方法名,如 com.co.cache.service.UserServiceImpl.getAllUser

*/

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是否为空

*/

public void afterPropertiesSet() throws Exception {

Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");

}

}

上面的代码中可以看到,在方法 public Object invoke(MethodInvocation invocation)中,完成了搜索 Cache/新建 cache的功能。

Element element = cache.get(cacheKey);

这句代码的作用是获取 cache中的 element,如果 cacheKey所对应的 element不存在,将会返回一个 null值。

Java代码

result = invocation.proceed();

这句代码的作用是获取所拦截方法的返回值,详细请查阅 AOP相关文档。随后,再建立一个拦截器 MethodCacheAfterAdvice,作用是在用户进行 create/update/delete操作时来刷新/remove相关 cache内容,这个拦截器实现了 AfterReturningAdvice接口,将会在所拦截的方法执行后执行在 public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3)方法中所预定的操作

Java代码

package com.co.cache.ehcache;

import java.lang.reflect.Method;

import java.util.List;

import net.sf.ehcache.Cache;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.util.Assert;

public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean

{

private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);

private Cache cache;

public void setCache(Cache cache) {

this.cache = cache;

}

public MethodCacheAfterAdvice() {

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);

logger.debug("remove cache " + cacheKey);

}

}

}

public void afterPropertiesSet() throws Exception {

Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");

}

}

上面的代码很简单,实现了 afterReturning方法实现自 AfterReturningAdvice接口,方法中所定义的内容将会在目标方法执行后执行,在该方法中的作用是获取目标 class的全名,如:com.co.cache.test.TestServiceImpl,然后循环 cache 的 key list,remove cache 中所有和该 class相关的 element。

Java代码

String className = arg3.getClass().getName();

随后,开始配置 ehCache的属性,ehCache需要一个 xml文件来设置 ehCache相关的一些属性,如最大缓存数量、cache刷新的时间等等.

ehcache.xml

<ehcache>

<diskStore path="c:\\myapp\\cache"/>

<defaultCache

maxElementsInMemory="1000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

/>

<cache name="DEFAULT_CACHE"

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="300000"

timeToLiveSeconds="600000"

overflowToDisk="true"

/>

</ehcache>

配置每一项的详细作用不再详细解释,有兴趣的请 google下 ,这里需要注意一点defaultCache标签定义了一个默认的 Cache,这个 Cache是不能删除的,否则会抛出 No default cache is configured异常。另外,由于使用拦截器来刷新 Cache内容,因此在定义cache生命周期时可以定义较大的数值,timeToIdleSeconds="300000",timeToLiveSeconds="600000",好像还不够大?然后,在将 Cache 和两个拦截器配置到 Spring,这里没有使用 2.0 里面 AOP的标签。

cacheContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<!--引用 ehCache的配置 -->

<bean id="defaultCacheManager"

/>

</property>

</bean>

<!-- flush cache拦截器 -->

<bean id="methodCacheAfterAdvice"

/>

</property>

</bean>

<bean id="methodCachePointCut"

encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<import resource="cacheContext.xml"/>

<bean id="testServiceTarget" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="target">

<ref local="testServiceTarget"/>

</property>

<property name="interceptorNames">

<list>

<value>methodCachePointCut</value>

<value>methodCachePointCutAdvice</value>

</list>

</property>

</bean>

</beans>

这里一定不能忘记 import cacheContext.xml文件,不然定义的两个拦截器就没办法使用

了。

最后,写一个测试的代码

MainTest.java

Java代码

(DEFAULT_CONTEXT_FILE);

TestService testService = (TestService)context.getBean("testService");

System.out.println("1--第一次查找并创建 cache");

testService.getAllObject();

System.out.println("2--在 cache中查找");

testService.getAllObject();

System.out.println("3--remove cache");

testService.updateObject(null);

System.out.println("4--需要重新查找并创建 cache");

testService.getAllObject();

}

}

运行,结果如下

Java代码

1--第一次查找并创建 cache

---TestService:Cache内不存在该 element,查找并放入 Cache!

2--在 cache中查找

3--remove cache

---TestService:更新了对象,这个 Class产生的 cache都将被 remove!

4--需要重新查找并创建 cache

---TestService:Cache内不存在该 element,查找并放入 Cache!

大功告成 .可以看到,第一步执行 getAllObject(),执行 TestServiceImpl内的方法,并创建了 cache,在第二次执行 getAllObject()方法时,由于 cache有该方法的缓存,直接从cache中 get出方法的结果,所以没有打印出 TestServiceImpl中的内容,而第三步,调用了 updateObject方法,和 TestServiceImpl相关的 cache被 remove,所以在第四步执行时,又执行 TestServiceImpl 中的方法,创建 Cache。网上也有不少类似的例子,但是很多都不是很完备,自己参考了一些例子的代码,其实在 spring-modules 中也提供了对几种 cache的支持,ehCache,OSCache,JBossCache这些,看了一下,基本上都是采用类似的方式,只不过封装的更完善一些,主要思路也还是 Spring的 AOP,有兴趣的可以研究一下。

热点排行