?

原文地址:
http://opensource.atlassian.com/confluence/spring/display/DISC/Caching+the+result+of+methods+using+Spring+and+EHCache


導(dǎo)言

Spring 1.1.1 開始, EHCache 就作為一種通用緩存解決方案集成進 Spring

我將示范攔截器的例子,它能把方法返回的結(jié)果緩存起來。

?

利用 Spring IoC 配置 EHCache

Spring 里配置 EHCache 很簡單。你只需一個 ehcache.xml 文件,該文件用于配置 EHCache

?

<ehcache>

??? <!—設(shè)置緩存文件 .data 的創(chuàng)建路徑。

???????? 如果該路徑是 Java 系統(tǒng)參數(shù),當前虛擬機會重新賦值。

???????? 下面的參數(shù)這樣解釋:
???????? user.home – 用戶主目錄
???????? user.dir????? – 用戶當前工作目錄
???????? java.io.tmpdir – 默認臨時文件路徑 -->
??? <diskStore path="java.io.tmpdir"/>


??? <!—缺省緩存配置。CacheManager 會把這些配置應(yīng)用到程序中。

??????? 下列屬性是 defaultCache 必須的:

??????? maxInMemory?????????? - 設(shè)定內(nèi)存中創(chuàng)建對象的最大值。
??????? eternal??????????????????????? - 設(shè)置元素(譯注:內(nèi)存中對象)是否永久駐留。如果是,將忽略超
??????????????????????????????????????????????時限制且元素永不消亡。
??????? timeToIdleSeconds? - 設(shè)置某個元素消亡前的停頓時間。
????????????????????????????????????????????? 也就是在一個元素消亡之前,兩次訪問時間的最大時間間隔值。
????????????????????????????????????????????? 這只能在元素不是永久駐留時有效(譯注:如果對象永恒不滅,則
????????????????????????????????????????????? 設(shè)置該屬性也無用)。
????????????????????????????????????????????? 如果該值是 0 就意味著元素可以停頓無窮長的時間。
??????? timeToLiveSeconds?- 為元素設(shè)置消亡前的生存時間。
?????????????????????????????????????????????? 也就是一個元素從構(gòu)建到消亡的最大時間間隔值。
?????????????????????????????????????????????? 這只能在元素不是永久駐留時有效。
??????? overflowToDisk??????? - 設(shè)置當內(nèi)存中緩存達到 maxInMemory 限制時元素是否可寫到磁盤
???????????????????????????????????????????????上。
??????? -->

??? <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

該攔截器實現(xiàn) org.aopalliance.intercept.MethodInterceptor 接口。一旦 運行起來 (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 代碼說明了:

  • 默認條件下,所有方法返回結(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ù)鈍化時間
??????? timeToLiveSeconds="120"???? //緩存數(shù)據(jù)的生存時間
??????? overflowToDisk="true"?????? //內(nèi)存不足時,是否啟用磁盤緩存
??? />
</ehcache>