设计思路:查询数据,通过AOP拦截需要缓存的方法,在方法进入之前进入拦截器,通过包、类、方法名称作为key获取当前缓存对象结果,如果为空,则执行真正的方法,如果有缓存对象,则直接返回。删除和修改数据,每次都在此之前调用拦截器方法清除相关缓存对象。
1.需要引入AOP和Ehcache相关Jar文件
2.编写切面类里面写上拦截时的业务逻辑
import java.io.Serializable; import java.util.List; 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; /** * * 类功能描述:AOP+Ehcache 拦截需要缓存的方法,插入点方法执行前后 * * @author <a href="mailto:[email protected]">mengqingyu </a> * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp $ * Create: 2011-12-12 下午04:27:44 */ 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 */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object result; String cacheKey = getCacheKey(invocation.getThis().getClass().getName() , invocation.getMethod().getName(), invocation.getArguments()); Element element = cache.get(cacheKey); logger.warn("检查缓存:" + cacheKey); if (element == null) { logger.warn("创建缓存......"); result = invocation.proceed(); element = new Element(cacheKey, (Serializable) result); cache.put(element); } List list = cache.getKeys(); return element.getValue(); } /** * * @function:获得cache key的方法,cache key是Cache中一个Element的唯一标识 * cache key包括 包名+类名+方法名,如com.co.cache.service.UserServiceImpl.getAllUser * @param targetName * @param methodName * @param arguments * @return * @author: mengqingyu 2011-12-12 下午04:35:21 */ private String getCacheKey(String targetName, String methodName, Object[] arguments) { StringBuilder sb = new StringBuilder(); sb.append(targetName).append(".").append(methodName); if (arguments != null) { for(Object o:arguments){ sb.append(".").append(o); } } return sb.toString(); } /** * implement InitializingBean,检查cache是否为空 */ public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "缓存未初始化"); } } package com.berheley.bi.aop; 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; /** * * 类功能描述:AOP+Ehcache 拦截需要缓存的方法,插入点方法执行之后 * * @author <a href="mailto:[email protected]">mengqingyu </a> * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp $ * Create: 2011-12-12 下午04:27:44 */ 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(); } /** * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在则删除结果, * 否则忽略,此方法为了保证更新数据库同时同步缓存 */ @Override public void afterReturning(Object arg, Method method, Object[] arguments, Object obj) throws Throwable { String className = obj.getClass().getName(); List<String> list = cache.getKeys(); for(String cacheKey:list){ if(cacheKey.startsWith(className)){ cache.remove(cacheKey); logger.debug("删除缓存:" + cacheKey); } } } /** * implement InitializingBean,检查cache是否为空 */ public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "缓存未初始化"); } }
3.配置文件中配置AOP和类的实例化
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- AOP+Ehcache by mengqingyu--> <!-- 引用ehCache的配置 --> <bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean> <!-- 定义ehCache的工厂,并设置所使用的Cache name 如找不到则使用默认缓存配置defaultCache--> <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager"/> </property> <property name="cacheName"> <value>systemCache</value> </property> </bean> <!-- find/create cache拦截器 --> <bean id="methodCacheInterceptor" class="com.berheley.bi.aop.MethodCacheInterceptor"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- find/create cache拦截器 --> <bean id="methodCacheAfterAdvice" class="com.berheley.bi.aop.MethodCacheAfterAdvice"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- 自动代理 --> <aop:config> <!-- 这里可添加多个切入点 --> <aop:advisor pointcut="execution(* com.berheley.bi.report.service.impl.SummaryReportServiceImpl.getSource*(..))" advice-ref="methodCacheInterceptor" /> <aop:advisor pointcut="execution(* com.berheley.bi.report.service.impl.SummaryReportServiceImpl.getPrefixAndSuffix*(..))" advice-ref="methodCacheAfterAdvice" /> </aop:config> </beans>
4.引入ehcache.xml文件
<!-- AOP+Ehcache by mengqingyu--> <!-- diskStore 当内存缓存数量超过设置数量时,将数据写到硬盘中,path为硬盘相对路径 maxElementsInMemory 缓存中最大允许创建的对象数 eternal 缓存中对象是否为永久,如果true过期设置将无效 timeToIdleSeconds 自最后读取开始有效时间 timeToLiveSeconds 自创建开始有效时间 overflowToDisk 内存不足时是否启用磁盘缓存 --> <ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <cache name="systemCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="259200000" timeToLiveSeconds="259200000" overflowToDisk="true"/> </ehcache>