成都心情

  BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  98 随笔 :: 2 文章 :: 501 评论 :: 1 Trackbacks

导言

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

/*
 * 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 代码说明了:

  • 默认条件下,所有方法返回结果都被缓存了( 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>

译注

如果你要缓存的方法是 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.blogjava.net/rosen


     <!--  EHCache  -->
    
< 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 >  
posted on 2005-08-12 09:41 Rosen 阅读(3678) 评论(6)  编辑  收藏 所属分类: 模式与策略

评论

# re: 利用 Spring 和 EHCache 缓存结果(翻译)[未登录] 2007-01-22 22:15 Nick
写的非常好 如果拦截器的控制可以更加灵活并实现部分可配置性就更好了
请问 hibernate里配置的只度  回复  更多评论
  

# re: 利用 Spring 和 EHCache 缓存结果(翻译)[未登录] 2007-01-22 22:17 Nick
请问如何实现hibernate里配置的只度cache 和读写cache的配置  回复  更多评论
  

# re: 利用 Spring 和 EHCache 缓存结果(翻译) 2007-01-24 16:34 Rosen
@Nick
只读,我理解为只要不删除cache就是只读的。读写我这里有个例子:
public class CleanCacheAdvice implements MethodBeforeAdvice {

private Cache cache;

public void setCache(Cache cache) {
this.cache = cache;
}

public void before(Method m, Object[] args, Object target) throws Throwable {
cache.removeAll();
}
}
当然,这样的控制不是很好,最好利用key进行删除。  回复  更多评论
  

# re: 利用 Spring 和 EHCache 缓存结果(翻译)[未登录] 2008-08-09 22:35 rain
你试过吗?为什么我按你的方法不行,没有起到缓存结果的作用。我用得spring是2.0  回复  更多评论
  

# re: 利用 Spring 和 EHCache 缓存结果(翻译) 2008-08-09 23:53 Rosen
@rain
当成功了,N年前就配置OK了,如果有问题请参考官方或者网络上的最新配置文章。本文在翻译的时候Spring还没到2呢。  回复  更多评论
  

# re: 利用 Spring 和 EHCache 缓存结果(翻译) 2008-11-24 11:21 wsqyc
如果不成功的话,可能有两种原因
1、正则表达式不对
2、你的被拦截的类不是通过Spring的getBean方法获取或注入的。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: