导言
从 Spring 1.1.1 开始, EHCache 就作为一种通用缓存解决方案集成进 Spring 。
我将示范拦截器的例子,它能把方法返回的结果缓存起来。
利用 Spring IoC 配置 EHCache
在 Spring 里配置 EHCache 很简单。你只需一个 ehcache.xml 文件,该文件用于配置 EHCache :
<ehcache>
<!—设置缓存文件 .data 的创建路径。
如果该路径是 Java 系统参数,当前虚拟机会重新赋值。
下面的参数这样解释: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> <diskStore path="java.io.tmpdir"/>
<!—缺省缓存配置。CacheManager 会把这些配置应用到程序中。
下列属性是 defaultCache 必须的:
maxInMemory - 设定内存中创建对象的最大值。 eternal - 设置元素(译注:内存中对象)是否永久驻留。如果是,将忽略超 时限制且元素永不消亡。 timeToIdleSeconds - 设置某个元素消亡前的停顿时间。 也就是在一个元素消亡之前,两次访问时间的最大时间间隔值。 这只能在元素不是永久驻留时有效(译注:如果对象永恒不灭,则 设置该属性也无用)。 如果该值是 0 就意味着元素可以停顿无穷长的时间。 timeToLiveSeconds - 为元素设置消亡前的生存时间。 也就是一个元素从构建到消亡的最大时间间隔值。 这只能在元素不是永久驻留时有效。 overflowToDisk - 设置当内存中缓存达到 maxInMemory 限制时元素是否可写到磁盘 上。 -->
<cache name="org.taha.cache.METHOD_CACHE" maxElementsInMemory="300" eternal="false" timeToIdleSeconds="500" timeToLiveSeconds="500" overflowToDisk="true" /> </ehcache> |
拦截器将使用 ”org.taha.cache.METHOD_CACHE” 区域缓存方法返回结果。下面利用 Spring IoC 让 bean 来访问这一区域。
<!-- ====================== 缓存 ======================= -->
<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>org.taha.cache.METHOD_CACHE</value> </property> </bean> |
构建我们的 MethodCacheInterceptor
该拦截器实现 org.aopalliance.intercept.MethodInterceptor 接口。一旦 运行起来 (kicks-in) ,它首先检查被拦截方法是否被配置为可缓存的。这将可选择性的配置想要缓存的 bean 方法。只要调用的方法配置为可缓存,拦截器将为该方法生成 cache key 并检查该方法返回的结果是否已缓存。如果已缓存,就返回缓存的结果,否则再次调用被拦截方法,并缓存结果供下次调用。
org.taha.interceptor.MethodCacheInterceptor
/* * Copyright 2002-2004 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package org.taha.interceptor;
import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log;
import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert;
import net.sf.ehcache.Cache; import net.sf.ehcache.Element;
/** * @author <a href=" mailto:irbouh@gmail.com">Omar Irbouh</a> * @since 2004.10.07 */ 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 void afterPropertiesSet() throws Exception { Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one."); }
/** * 主方法 * 如果某方法可被缓存就缓存其结果 * 方法结果必须是可序列化的(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;
logger.debug("looking for method result in cache"); String cacheKey = getCacheKey(targetName, methodName, arguments); Element element = cache.get(cacheKey); if (element == null) { //call target/sub-interceptor logger.debug("calling intercepted method"); result = invocation.proceed();
//cache method result logger.debug("caching result"); 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(); } } |
MethodCacheInterceptor 代码说明了:
- 默认条件下,所有方法返回结果都被缓存了( methodNames 是 null )
- 缓存区利用 IoC 形成
- cacheKey 的生成还包括方法参数的因素(译注:参数的改变会影响 cacheKey )
使用 MethodCacheInterceptor
下面摘录了怎样配置 MethodCacheInterceptor :
<bean id="methodCacheInterceptor" class="org.taha.interceptor.MethodCacheInterceptor"> <property name="cache"> <ref local="methodCache" /> </property> </bean>
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="methodCacheInterceptor"/> </property> <property name="patterns"> <list> <value>.*methodOne</value> <value>.*methodTwo</value> </list> </property> </bean>
<bean id="myBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <bean class="org.taha.beans.MyBean"/> </property> <property name="interceptorNames"> <list> <value>methodCachePointCut</value> </list> </property> </bean> |
译注
如果你要缓存的方法是 findXXX,那么正则表达式应该这样写:“.*find.*”。
夏昕所著《 Hibernate 开发指南》,其中他这样描述 EHCache 配置文件的:
<ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" //Cache中最大允许保存的数据数量 eternal="false" //Cache中数据是否为常量 timeToIdleSeconds="120" //缓存数据钝化时间 timeToLiveSeconds="120" //缓存数据的生存时间 overflowToDisk="true" //内存不足时,是否启用磁盘缓存 /> </ehcache> |
请注意!引用、转贴本文应注明原译者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen
<!-- EHCache -->
< 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 > org.taha.cache.METHOD_CACHE </ value >
</ property >
</ bean >
< bean id ="methodCacheInterceptor" class ="com.cdmcs.util.MethodCacheInterceptor" >
< property name ="cache" >
< ref local ="methodCache" />
</ property >
</ bean >
< bean id ="cleanCacheAdvice" class ="com.cdmcs.util.CleanCacheAdvice" >
< property name ="cache" >
< ref local ="methodCache" />
</ property >
</ bean >
< bean id ="advicePointCut" class ="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
< property name ="advice" >
< ref local ="cleanCacheAdvice" />
</ property >
< property name ="patterns" >
< list >
< value > .*save.* </ value >
< value > .*update.* </ value >
< value > .*delete.* </ value >
< value > .*valid.* </ value >
</ list >
</ property >
</ bean >
< bean id ="methodCachePointCut" class ="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
< property name ="advice" >
< ref local ="methodCacheInterceptor" />
</ property >
< property name ="patterns" >
< list >
< value > .*find.* </ value >
</ list >
</ property >
</ bean >
<!-- EHCache End -->
< bean id ="supplyDemandService" parent ="baseTransactionProxy" >
< property name ="target" >
< bean class ="com.cdmcs.webbuilder.service.impl.SupplyDemandServiceImpl" autowire ="byName" />
</ property >
< property name ="preInterceptors" >
< list >
< ref bean ="methodCachePointCut" />
< ref bean ="advicePointCut" />
</ list >
</ property >
</ bean >