導言

從 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

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="Omar''''>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 >

 譯注

夏昕所著《Hibernate 開發指南》,其中他這樣描述 EHCache 配置文件的: 

< ehcache >
    
< diskStore path = " java.io.tmpdir " />
    
< defaultCache
        maxElementsInMemory
= " 10000 "   // Cache中最大允許保存的數據數量
        eternal = " false "                         // Cache中數據是否為常量
        timeToIdleSeconds = " 120 "       // 緩存數據鈍化時間
        timeToLiveSeconds = " 120 "       // 緩存數據的生存時間
        overflowToDisk = " true "         // 內存不足時,是否啟用磁盤緩存
     />
</ ehcache >