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一次