缓存方法 : Srping+Ehcache 在Service层配置缓存

自定义Key
package cn.com.voge.system.service;

import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;

/**
 * 项目名称: wp_idea_linux
 * 功能说明:
 * 创建者: Pandy,
 * 邮箱: [email protected], [email protected]
 * 版权:
 * 官网:
 * 创建日期: 15-9-24.
 * 创建时间: 上午11:25.
 * 修改历史:
 * -----------------------------------------------
 */
public class RhKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object o, Method method, Object... objects) {
        return o.toString();
    }
}

<bean id="customGenerator" class="cn.com.voge.system.service.RhKeyGenerator">
<cache:annotation-driven cache-manager="cacheManager" key-generator="customGenerator" proxy-target-class="true"/>



@Cacheable 支持如下几个参数:
value:缓存位置名称,不能为空,如果使用EHCache,就是ehcache.xml中声明的cache的name
key: 缓存的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL
condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL


奇葩问题:
@Cacheable(value="h1m0Cache", key="#params.toString() + ',' + #sessionData.getInstanceId()")
public ControllerContext setDropDownData(ArrayList<Map<String, Object>> params, SessionData sessionData,final String cacheKey) {
}

, 在开发环境下没问题,但是打包之后就出现问题 Method call: Attempted to call method toString() on null context object
=====================> 一直找不到问题原因,params参数不可能是null, 难道不能使用方法里面的参数来做key? http://hanqunfeng.iteye.com/blog/1158824, 这个文章已经推翻了, 可是为什么出现问题呢?


SpringMVC整合Ehcache(注解方式): http://blog.csdn.net/jadyer/article/details/12257865, 介绍很详细.
注释驱动的 Spring cache 缓存介绍 https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/, 里面介绍了注解和配置.

@CachePut 应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存
@CacheEvict 即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据
@Cacheable 应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中



@Cacheable 注释,则当重复使用相同参数调用方法的时候,方法本身不会被调用执行,即方法本身被略过了,取而代之的是方法的结果直接从缓存中找到并返回了。
@Cacheable(value="accountCache"): 使用accountCache缓存.
@CacheEvict(value=”accountCache”,key=”#account.getName()”),其中的 Key 是用来指定缓存的 key 的,这里因为我们保存的时候用的是 account 对象的 name 字段,所以这里还需要从参数 account 对象中获取 name 的值来作为 key,前面的 # 号代表这是一个 SpEL 表达式,此表达式可以遍历方法的参数对象,具体语法可以参考 Spring 的相关文档手册。
@Cacheable(value="accountCache",condition="#userName.length() <= 4") condition=”#userName.length() <=4”,这里使用了 SpEL 表达式访问了参数 userName 对象的 length() 方法,条件表达式返回一个布尔值,true/false,当条件为 true,则进行缓存操作,否则直接调用方法执行的返回结果。
@Cacheable(value="accountCache",key="#userName.concat(#password)")  key 属性,其中引用了方法的两个参数 userName 和 password

@CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中。现实中并不总是如此,有些情况下我们希望方法一定会被调用,因为其除了返回一个结果,还做了其他事情,例如记录日志,调用接口等,这个时候,我们可以用 @CachePut 注释
@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache 
 public Account getAccountByName(String userName) { 
   // 方法内部实现不考虑缓存逻辑,直接实现业务
   return getFromDB(userName); 
 } 
 @CachePut(value="accountCache",key="#account.getName()")// 更新 accountCache 缓存
 public Account updateAccount(Account account) { 
   return updateDB(account); 
 } 
 private Account updateDB(Account account) { 
   System.out.println("real updating db..."+account.getName()); 
   return account; 
 }


注解碰到的问题:
Cannot find cache named xxxxxx CacheableOperation http://m.bianceng.cn/Programming/Java/201309/37363_10.htm
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
		<property name="configLocation" value="classpath:ehcache.xml"/>
	</bean>
	<bean id="cacheManager1" class="org.springframework.cache.ehcache.EhCacheCacheManager">
		<property name="cacheManager" ref="cacheManagerFactory"/>
	</bean>
	<bean id="cacheManager"
		  class="org.springframework.cache.support.CompositeCacheManager">
		<property name="cacheManagers">
			<list>
				<ref bean="cacheManager1" />
			</list>
		</property>
		<property name="fallbackToNoOpCache" value="true" />
	</bean>
	<cache:annotation-driven cache-manager="cacheManager"/>

在找不到 accountCache,且没有将 fallbackToNoOpCache 设置为 true 的情况下,系统会抛出异常。



原文: http://miaoxianjie.iteye.com/blog/1700379
有些地方做了适当修改
1. ehcache 文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">  
  
    <diskStore path="java.io.tmpdir"/>  
    <defaultCache  
            maxElementsInMemory="10000"  
            eternal="false"  
            timeToIdleSeconds="520"  
            timeToLiveSeconds="520"  
            overflowToDisk="true"  
            maxElementsOnDisk="10000000"  
            diskPersistent="false"  
            diskExpiryThreadIntervalSeconds="120"  
            memoryStoreEvictionPolicy="LRU"  
     />  
  
       
    <cache name="testServiceCache"  
            maxElementsInMemory="10"  
            eternal="false"  
            timeToIdleSeconds="200"  
            timeToLiveSeconds="300"  
            overflowToDisk="true"  
            />      
              
</ehcache>  


2. applicationContext.xml 文件 相关代码
   <!-- Service Cache   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>testServiceCache</value>  
       </property>  
   </bean>  
     
<!-- methodCacheInterceptor 要自己实现的代码 -->  
   <bean id="methodCacheInterceptor" class="com.miao.service.MethodCacheInterceptor">  
       <property name="cache">  
           <ref local="methodCache"/>  
       </property>  
   </bean>  
     
   <!-- 配置要拦截的service, 拦截service包下所有类的所有方法  -->  
   <aop:config>  
    <aop:pointcut id="methodCachePointCut" expression="execution(* com.miao.service.*.*(..))" />  
    <aop:advisor pointcut-ref="methodCachePointCut" advice-ref="methodCacheInterceptor" />  
</aop:config>  
     
<!--  也可用此种方式 指定哪些方法使用cache-->  
   <!--   
   <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
       <property name="advice">  
           <ref local="methodCacheInterceptor"/>  
       </property>  
       <property name="patterns">  
           <list>  
               <value>.*getStepPartKitList</value>  
           </list>  
       </property>  
   </bean>  
-->  


3.MethodCacheInterceptor
package cn.com.voge.system.interceptor;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;

import java.io.Serializable;

/**
 * 项目名称: wp_idea_linux
 * 功能说明:
 * 创建者: Pandy,
 * 邮箱: [email protected], [email protected]
 * 版权:
 * 官网:
 * 创建日期: 15-9-16.
 * 创建时间: 下午4:31.
 * 修改历史:
 * -----------------------------------------------
 */
public class MethodCacheInterceptor implements MethodInterceptor,InitializingBean {
    //private static final Log LOGGER = LogFactory.getLog(MethodCacheInterceptor.class);

    private Ehcache cache;//我使用的Spring版本使用Ehcache类,一些可能是使用Cache类.注意一下.

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


    public Object invoke(MethodInvocation invocation) throws Throwable {

        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] arguments = invocation.getArguments();
        Object result;

        String cacheKey = getCacheKey(targetName, methodName, arguments);
        Element element = cache.get(cacheKey);
        System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        if (element == null) {
            result = invocation.proceed();   //调用被拦截的目标方法
            //LOGGER.info("Set into Cache");
            element = new Element(cacheKey, (Serializable) result);
            cache.put(element);
        }
        return element.getValue();
    }


    private String getCacheKey(String targetName, String methodName,Object[] arguments) {
        StringBuffer sb = new StringBuffer();
        //拼cacheKey 这里使用 className.methodName(arg1,arg2.....argN) 作为key
        sb.append(targetName).append(".").append(methodName).append("(");
        if ((arguments != null) && (arguments.length != 0)) {
            for (int i = 0; i < arguments.length; i++) {
                sb.append(arguments[i]);
                if (i + 1 != arguments.length) {
                    sb.append(",");
                }
            }
        }
        sb.append(")");
        return sb.toString();
    }

    public void afterPropertiesSet() throws Exception {
        if(null == cache) {
            throw new IllegalArgumentException("Cache should not be null.");
        }
    }

}

 

4.使用Junit测试即可,传递相同的参数,调用service内的同一个方法,只会进入DAO一次

猜你喜欢

转载自panyongzheng.iteye.com/blog/2234167