导言
从
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
>