導言
從
Spring 1.1.1
開始,
EHCache
就作為一種通用緩存解決方案集成進
Spring
。
我將示范攔截器的例子,它能把方法返回的結果緩存起來。
利用
Spring IoC
配置
EHCache
在 Spring 里配置 EHCache 很簡單。你只需一個 ehcache.xml 文件,該文件用于配置 EHCache :
<ehcache>
??? <!—設置緩存文件 .data 的創建路徑。
???????? 如果該路徑是 Java 系統參數,當前虛擬機會重新賦值。
???????? 下面的參數這樣解釋:
??????? 下列屬性是 defaultCache 必須的:
??????? maxInMemory?????????? - 設定內存中創建對象的最大值。
??? <cache name="org.taha.cache.METHOD_CACHE"
?
???????? user.home – 用戶主目錄
???????? user.dir????? – 用戶當前工作目錄
???????? java.io.tmpdir – 默認臨時文件路徑 -->
??? <diskStore path="java.io.tmpdir"/>
??? <!—缺省緩存配置。CacheManager 會把這些配置應用到程序中。
??????? eternal??????????????????????? - 設置元素(譯注:內存中對象)是否永久駐留。如果是,將忽略超
??????????????????????????????????????????????時限制且元素永不消亡。
??????? timeToIdleSeconds? - 設置某個元素消亡前的停頓時間。
????????????????????????????????????????????? 也就是在一個元素消亡之前,兩次訪問時間的最大時間間隔值。
????????????????????????????????????????????? 這只能在元素不是永久駐留時有效(譯注:如果對象永恒不滅,則
????????????????????????????????????????????? 設置該屬性也無用)。
????????????????????????????????????????????? 如果該值是 0 就意味著元素可以停頓無窮長的時間。
??????? timeToLiveSeconds?- 為元素設置消亡前的生存時間。
?????????????????????????????????????????????? 也就是一個元素從構建到消亡的最大時間間隔值。
?????????????????????????????????????????????? 這只能在元素不是永久駐留時有效。
??????? overflowToDisk??????? - 設置當內存中緩存達到 maxInMemory 限制時元素是否可寫到磁盤
???????????????????????????????????????????????上。
??????? -->
??????? 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">
<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
???
? <property name="configLocation">
??? <value>classpath:ehcache.xml</value>
? </property>
</bean>
? <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
/*
package org.taha.interceptor;
import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import net.sf.ehcache.Cache;
/**
? private Cache cache;
? /**
? /**
? /**
??? logger.debug("looking for method result in cache");
????? //cache method result
? /**
??? return sb.toString();
?
?* 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.
?*/
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.springframework.util.Assert;
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);
?? * 設置緩存名
?? */
? 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;
??? 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();
????? 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]);
????? }
??? }
? }
}
MethodCacheInterceptor
代碼說明了:
-
默認條件下,所有方法返回結果都被緩存了(
methodNames
是
null
)
-
緩存區利用
IoC
形成
-
cacheKey
的生成還包括方法參數的因素(譯注:參數的改變會影響
cacheKey
)
使用
MethodCacheInterceptor
下面摘錄了怎樣配置 MethodCacheInterceptor :
<bean id="methodCacheInterceptor" class="org.taha.interceptor.MethodCacheInterceptor">
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<bean id="myBean" class="org.springframework.aop.framework.ProxyFactoryBean">
? <property name="cache">
??? <ref local="methodCache" />
? </property>
</bean>
? <property name="advice">
??? <ref local="methodCacheInterceptor"/>
? </property>
? <property name="patterns">
??? <list>
????? <value>.*methodOne</value>
????? <value>.*methodTwo</value>
??? </list>
? </property>
</bean>
? <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.aygfsteel.com/rosen
???? < 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 > ?