导言

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