?
原文地址:
http://opensource.atlassian.com/confluence/spring/display/DISC/Caching+the+result+of+methods+using+Spring+and+EHCache
導(dǎo)言
從
Spring 1.1.1
開始,
EHCache
就作為一種通用緩存解決方案集成進(jìn)
Spring
。
我將示范攔截器的例子,它能把方法返回的結(jié)果緩存起來。
?
利用
Spring IoC
配置
EHCache
在
Spring
里配置
EHCache
很簡單。你只需一個(gè)
ehcache.xml
文件,該文件用于配置
EHCache
:
?
<ehcache>
??? <!—設(shè)置緩存文件 .data 的創(chuàng)建路徑。
???????? 如果該路徑是 Java 系統(tǒng)參數(shù),當(dāng)前虛擬機(jī)會重新賦值。
???????? 下面的參數(shù)這樣解釋: ???????? user.home – 用戶主目錄 ???????? user.dir????? – 用戶當(dāng)前工作目錄 ???????? java.io.tmpdir – 默認(rèn)臨時(shí)文件路徑 --> ??? <diskStore path="java.io.tmpdir"/>
??? <!—缺省緩存配置。CacheManager 會把這些配置應(yīng)用到程序中。
??????? 下列屬性是 defaultCache 必須的:
??????? maxInMemory?????????? - 設(shè)定內(nèi)存中創(chuàng)建對象的最大值。 ??????? eternal??????????????????????? - 設(shè)置元素(譯注:內(nèi)存中對象)是否永久駐留。如果是,將忽略超 ??????????????????????????????????????????????時(shí)限制且元素永不消亡。 ??????? timeToIdleSeconds? - 設(shè)置某個(gè)元素消亡前的停頓時(shí)間。 ????????????????????????????????????????????? 也就是在一個(gè)元素消亡之前,兩次訪問時(shí)間的最大時(shí)間間隔值。 ????????????????????????????????????????????? 這只能在元素不是永久駐留時(shí)有效(譯注:如果對象永恒不滅,則 ????????????????????????????????????????????? 設(shè)置該屬性也無用)。 ????????????????????????????????????????????? 如果該值是 0 就意味著元素可以停頓無窮長的時(shí)間。 ??????? timeToLiveSeconds?- 為元素設(shè)置消亡前的生存時(shí)間。 ?????????????????????????????????????????????? 也就是一個(gè)元素從構(gòu)建到消亡的最大時(shí)間間隔值。 ?????????????????????????????????????????????? 這只能在元素不是永久駐留時(shí)有效。 ??????? overflowToDisk??????? - 設(shè)置當(dāng)內(nèi)存中緩存達(dá)到 maxInMemory 限制時(shí)元素是否可寫到磁盤 ???????????????????????????????????????????????上。 ??????? -->
??? <cache name="org.taha.cache.METHOD_CACHE" ??????? maxElementsInMemory="300" ??????? eternal="false" ??????? timeToIdleSeconds="500" ??????? timeToLiveSeconds="500" ??????? overflowToDisk="true" ??????? /> </ehcache>
|
?
攔截器將使用
”org.taha.cache.METHOD_CACHE”
區(qū)域緩存方法返回結(jié)果。下面利用
Spring IoC
讓
bean
來訪問這一區(qū)域。
?
<!-- ======================?? 緩存?? ======================= -->
<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>
|
?
構(gòu)建我們的
MethodCacheInterceptor
該攔截器實(shí)現(xiàn)
org.aopalliance.intercept.MethodInterceptor
接口。一旦
運(yùn)行起來
(kicks-in)
,它首先檢查被攔截方法是否被配置為可緩存的。這將可選擇性的配置想要緩存的
bean
方法。只要調(diào)用的方法配置為可緩存,攔截器將為該方法生成
cache key
并檢查該方法返回的結(jié)果是否已緩存。如果已緩存,就返回緩存的結(jié)果,否則再次調(diào)用被攔截方法,并緩存結(jié)果供下次調(diào)用。
?
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;
? /** ?? * 設(shè)置緩存名 ?? */ ? public void setCache(Cache cache) { ??? this.cache = cache; ? }
? /** ?? * 檢查是否提供必要參數(shù)。 ?? */ ? public void afterPropertiesSet() throws Exception { ??? Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one."); ? }
? /** ?? * 主方法 ?? * 如果某方法可被緩存就緩存其結(jié)果 ?? * 方法結(jié)果必須是可序列化的(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
代碼說明了:
-
默認(rèn)條件下,所有方法返回結(jié)果都被緩存了(
methodNames
是
null
)
-
緩存區(qū)利用
IoC
形成
-
cacheKey
的生成還包括方法參數(shù)的因素(譯注:參數(shù)的改變會影響
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>
|
?
?
譯注
夏昕所著《
Hibernate
開發(fā)指南》,其中他這樣描述
EHCache
配置文件的:
?
<ehcache> ??? <diskStore path="java.io.tmpdir"/> ??? <defaultCache ??????? maxElementsInMemory="10000" //Cache中最大允許保存的數(shù)據(jù)數(shù)量 ??????? eternal="false"????????????????????? ?//Cache中數(shù)據(jù)是否為常量 ??????? timeToIdleSeconds="120"???? //緩存數(shù)據(jù)鈍化時(shí)間 ??????? timeToLiveSeconds="120"???? //緩存數(shù)據(jù)的生存時(shí)間 ??????? overflowToDisk="true"?????? //內(nèi)存不足時(shí),是否啟用磁盤緩存 ??? /> </ehcache>
|