继续上篇,这篇介绍服务层缓存,基于aop的方式使用ehcache
一、修改配置文件
修改spring-context-ehcache.xml文件,加入:
<!-- ehcache缓存实例 -->
<bean id="testCacheInterceptor" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="ehcacheManager"/>
<property name="cacheName" value="aopTestDao" />
</bean>
<!-- 要织入的通知(切面) -->
<bean id="testCache" class="org.xs.demo1.CacheInterceptor">
<!-- 传入ehcache缓存实例 -->
<property name="cache" ref="testCacheInterceptor" />
</bean>
<!-- AOP配置 -->
<aop:config>
<!-- 定义切面 -->
<aop:aspect ref="testCache">
<!-- 定义增加缓存的切点 -->
<aop:pointcut id="testAddCache" expression="execution(* org.xs.demo1.testDao.get*(..))" />
<!-- 定义环绕通知 -->
<aop:around pointcut-ref="testAddCache" method="addCache" />
<!-- 定义移除缓存的切点 -->
<aop:pointcut id="testRemoveCache" expression="execution(* org.xs.demo1.testDao.update*(..)) || execution(* org.xs.demo1.testDao.delete*(..))" />
<!-- 定义后置通知 -->
<aop:after pointcut-ref="testRemoveCache" method="removeCache" />
</aop:aspect>
</aop:config>
在ehcache-context.xml中也可以加入cacheName为"aopTestDao"的缓存实例配置
二、增加缓存操作拦截器
在"src/main/java"代码文件夹的"org.xs.demo1"的包下新建"CacheInterceptor.java"类:
package org.xs.demo1;
import java.io.Serializable;
import java.util.List;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 缓存操作拦截器
*/
public class CacheInterceptor {
/**
* 缓存实例
*/
private Cache cache;
public Cache getCache() {
return cache;
}
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* 增加缓存
*/
public Object addCache(ProceedingJoinPoint joinpoint) {
Object result = null;
try {
//组合缓存key(实例名,方法名,方法参数列表)
String cacheKey = getCacheKey(joinpoint.getTarget().getClass().getName(), joinpoint.getSignature().getName(), joinpoint.getArgs());
Element element = cache.get(cacheKey);
//如果缓存里有就从缓存里取
if(element != null) {
result = element.getObjectValue();
} else {
//执行方法,获得结果
result = joinpoint.proceed();
//将结果存入缓存
cache.put(new Element(cacheKey, (Serializable)result));
}
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
/**
* 移除缓存
*/
public void removeCache(JoinPoint point) {
//获得实例名
String className = point.getTarget().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);
}
}
}
/**
* 组合缓存key
*/
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++) {
if(arguments[i] instanceof String[]){
String[] strArray = (String[])arguments[i];
sb.append(".");
for(String str : strArray){
sb.append(str);
}
}else{
sb.append(".").append(arguments[i]);
}
}
}
return sb.toString();
}
}
三、运行测试
1、先注释之前testDao里的ehcache注释,不然看不到效果
@SuppressWarnings("unchecked")
/**@Cacheable(value="testDao", key="'list'")*/
public List<testInfo> getList() {
String hql = "from testInfo";
Query query = sessionFactory.getCurrentSession().createQuery(hql);
query.setCacheable(true);
return query.list();
}
/**@Cacheable(value="testDao", key="'view' + #id")*/
public testInfo getInfo(String id) {
return (testInfo) sessionFactory.getCurrentSession().get(testInfo.class, id);
}
/**@Caching(
put={@CachePut(value="testDao", key="'view' + #testInfo.id")},
evict={@CacheEvict(value="testDao", key="'list'")}
)*/
public testInfo update(testInfo testInfo) {
testInfo.setName("789");
//update
return testInfo;
}
/**@Caching(
evict={
@CacheEvict(value="testDao", key="'view' + #id"),
@CacheEvict(value="testDao", key="'list'")}
)*/
public void delete(String id) {
//delete
}
/**@CacheEvict(value="testDao", allEntries=true)*/
public void deleteAll() {
//deleteAll
}
2、测试
第一次访问"http://localhost:8080/demo1/hello/list2"地址,进入增加缓存的环绕通知,然后执行方法,将结果存入缓存
第二次访问的时候,缓存里就有数据了,可以直接用,不用再执行方法
点击"删除"按钮,会进入移除缓存的后置通知,然后匹配实例名,删除list的缓存
实例代码地址:https://github.com/ctxsdhy/cnblogs-example