Spring AOP+EHCache简单缓存系统解决方案
需要使用Spring来实现一个Cache简单的解决方案,具体需求如下:使用任意一个现有开源Cache
Framework,要求可以Cache系统中Service或则DAO层的get/find等方法返回结果,如果数据更新(使用Create/update/delete方法),则刷新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的功能。
Java代码
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接口,方法中所定义的内容将会在目标方法执行后执行,在该方法中
Java代码
String className =
arg3.getClass().getName();
的作用是获取目标class的全名,如:com.co.cache.test.TestServiceImpl,然后循环cache的key
list,remove cache中所有和该class相关的element。
随后,开始配置ehCache的属性,ehCache需要一个xml文件来设置ehCache相关的一些属性,如最大缓存数量、cache刷新的时间等等.
ehcache.xml
Java代码
<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>