導言
從 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> ?
<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> ???
<!-- ======================?? 緩存?? ======================= -->
<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();? }} ?
/*?* 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 代碼說明了:
使用 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>
<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.tkk7.com/rosen
Powered by: BlogJava Copyright © Rosen