在实际的项目开发中,肯定会根据具体业务,数据大小,复杂度采用不同的技术实现方式,Ehcache在实际项目开发中一般被用来缓存方法结果集,且可以与Spring无缝集成,完全交由Spring——Aop拦截器来完成,我们只需处理好业务数据获取环节。
1.ehcache.xml配置:
<ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <cache name="gov.csc.ems.cache.METHOD_CACHE" maxElementsInMemory="300" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>
2. Spring集成Ehcache配置,cacheManage,methodCache
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean> <bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="cacheManager" /> </property> <property name="cacheName"> <value>gov.csc.ems.cache.METHOD_CACHE</value> </property> </bean>
3. 完成上面的基础配置,Spring是靠拦截器来缓存我们的方法,因此建立我们自己的方法拦截器MethodCacheInterceptor。MethodCacheInterceptor实现了org.aopalliance.intercept.MethodInterceptor接口。
import java.io.Serializable; 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 MethodCacheInterceptor implements MethodInterceptor, InitializingBean { private Cache cache; /** * sets cache name to be used */ public void setCache(Cache cache) { this.cache = cache; } /** * Checks if required attributes are provided. */ public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one."); } /** * main method caches method result if method is configured for caching * method results must be serializable */ public Object invoke(MethodInvocation invocation) throws Throwable { String targetName = invocation.getThis().getClass().getName(); String methodName = invocation.getMethod().getName(); Object[] arguments = invocation.getArguments(); Object result; String cacheKey = getCacheKey(targetName, methodName, arguments); Element element = cache.get(cacheKey); if (element == null) { result = invocation.proceed(); element = new Element(cacheKey, (Serializable) result); cache.put(element); } return element.getValue(); } /** * creates cache key: targetName.methodName.argument0.argument1... */ 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(); } }
invoke方法中:
String cacheKey = getCacheKey(targetName, methodName, arguments); Element element = cache.get(cacheKey); if (element == null) { result = invocation.proceed(); element = new Element(cacheKey, (Serializable) result); cache.put(element); } return element.getValue();
这段代码就是用来缓存我们的方法结果,拦截器先用key(key=className + methodName + arguments)查询缓存,缓存中存在则返回,否则调用invocation.proceed(),根据我们自己的实现查询数据,即第一次查询,以后每次就走缓存。
4.Spring中定义拦截器配置:
<bean id="methodCacheInterceptor" class="gov.csc.ems.util.cache.interceptor.MethodCacheInterceptor"> <property name="cache"> <ref local="methodCache" /> </property> </bean>
5.Aop正则表达式的切入点配置:
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="methodCacheInterceptor" /> </property> <property name="patterns"> <list> <value>.*</value> </list> </property> </bean>
patterns要拦截的规则,可以拦截指定的方法,如.getOrg*,.getUser*,这里我拦截所有的方法。
6.交给Spring代理
<bean id="codeCacheBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <bean class="gov.csc.ems.util.cache.CodeCache" /> </property> <property name="interceptorNames"> <list> <value>methodCachePointCut</value> </list> </property> </bean>
其中<bean class="gov.csc.ems.util.cache.CodeCache" />它就是我们缓存的类,缓存就是它里面的所有方法,当我们要用到它里面的方法时,如果此方法配置了缓存拦截,就会先走拦截器。
7.要缓存的方法类CodeCache
public class CodeCache { public CodeCache() { super(); } public Map getOrgCache() { //从数据库查询org ...... } public Map getUserCache() { //从数据库查询user ...... } }
8.测试
我这里写了个servlet,根据Spring获取到CodeCache,
CodeCache codeCache=(CodeCache) BeanUtil.getBean("codeCacheBean"); Map codeBasOrgs=codeCache.getOrgCache(); System.out.println(codeBasOrgs.get("0001"));
分别在拦截器和CodeCache中打上断点,运行发现每次先走拦截器,而且第一次会走CodeCache查询,以后就直接取缓存了。
9.缓存的更新
如新添加了Org或user,CodeCache.getOrgCache.put(key,value)即可添加到缓存。