自定义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一次