ehcache.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />
<!-- 配置自定义缓存 maxElementsInMemory:缓存中允许创建的最大对象数 eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前, 两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,
如果该值是 0 就意味着元素可以停顿无穷长的时间。 timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,
这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。 overflowToDisk:内存不足时,是否启用磁盘缓存。 memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。 -->
<cache name="mobileCache" maxElementsInMemory="10000" eternal="false"
overflowToDisk="true" timeToIdleSeconds="1800" timeToLiveSeconds="3600"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
spring配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.wallet.*" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="wallet-web"></property>
<!-- value 对应persistence.xml中的 persistence-unit name -->
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<span style="color:#FF9966;"><strong><!-- 配置eh缓存管理器 -->
<!--配置缓存管理器 -->
<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>com.easyway.MethodCache</value>
</property>
</bean>
<!-- 自定义缓存拦截器 -->
<bean id="methodCacheInterceptor" class="com.wallet.core.intercepter.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>
<!-- 下面的配置就使得在数据访问时,cache将拦截从数据库获取的数据,与cache数据比较,如有就不放入cache,没有就放入,更新到数据库去,也是先存入cache,再更新到数据库中去 -->
<property name="patterns">
<list>
<value>.*findAll</value>
<value>.*selectByCondis</value>
<value>.*selectAll</value>
</list>
</property>
</bean>
<!-- flush cache拦截器 -->
<bean id="methodCacheAfterAdvice" class="com.wallet.core.intercepter.MethodCacheAfterAdvice">
<property name="cache">
<ref local="methodCache" />
</property>
</bean>
<bean id="methodCachePointCutAdvice"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheAfterAdvice" />
</property>
<property name="patterns">
<list>
<value>.*add</value>
</list>
</property>
</bean>
<!-- 声明一个服务 -->
<bean id="cascadeEventServiceTarget"
class="com.wallet.myWallet.service.impl.CascadeEventServiceImpl" />
<bean id="fixedExpensesAccountServiceTarget"
class="com.wallet.myWallet.service.impl.FixedExpensesAccountServiceImpl" />
<!-- 相关的服务 -->
<bean id="cascadeEventService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="cascadeEventServiceTarget" />
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
</list>
</property>
</bean>
<bean id="fixedExpensesAccountService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="fixedExpensesAccountServiceTarget" />
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
<value>methodCachePointCutAdvice</value>
</list>
</property>
</bean></strong></span>
<tx:annotation-driven transaction-manager="txManager" />
</beans>
spring与ehcache集成的相关配置主要在上色不分,其中主要用到了两个拦截器一个是 methodCacheInterceptor主要用于对查询数据进行缓存; methodCacheAfterAdvice主要用于对save/update/delete的方法进行缓存。
methodCacheInterceptor
/**
* 方法拦截器,主要针对查询方法进行缓存,
* 用户每次查询会现在缓存中根据cacheKey查找相应的数据,
* 有的话就只从缓存中取,没有的话就去数据库中查找,并将查询结果存到缓存中
*/
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
private static final Logger log = Logger.getLogger(MethodCacheInterceptor.class);
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public void afterPropertiesSet() throws Exception {
log.info(cache + " A cache is required. Use setCache(Cache) to provide one.");
}
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 = null;
synchronized (this) {
element = cache.get(cacheKey);
if (element == null) {
log.info(cacheKey + "加入到缓存: " + cache.getName());
System.out.println("加入到缓存");
// 调用实际的方法
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
} else {
log.info(cacheKey + "使用缓存: " + cache.getName());
System.out.println("使用缓存");
}
}
return element.getValue();
}
/**
* <b>function:</b> 返回具体的方法全路径名称 参数
*
* @author hoojo
* @createDate 2012-7-2 下午06:12:39
* @param targetName 全路径
* @param methodName 方法名称
* @param arguments 参数
* @return 完整方法名称
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws IllegalArgumentException
*/
private String getCacheKey(String targetName, String methodName, Object[] arguments)
throws Exception {
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(getArgument(arguments[i]));
}
}
return sb.toString();
}
/**
* 因为查询的参数是一个实体类,所以通过反射的方式获取实体类中的属性以及属性值作为cacheKey的一部分
*
* @param object
* @return
* @throws InstantiationException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws NoSuchFieldException
* @throws SecurityException
*/
private String getArgument(Object object) throws Exception {
StringBuffer sb = new StringBuffer();
if(object!=null && !"".equals(object)){
System.out.println(object.getClass().getName());
Method[] methods = object.getClass().getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("get")) {
String fieldName = m.getName().substring(3, m.getName().length()).toLowerCase();
Object obj1 = m.invoke(object, null);
if (obj1 != null && !"".equals(obj1)) {
sb.append(fieldName).append(obj1);
}
}
}
}
return sb.toString();
}
}
methodCacheAfterAdvice
/**
* 拦截器MethodCacheAfterAdvice,作用是在用户进行create/update/delete操作时来刷新/remove相关cache内容,
* 这个拦截器实现了AfterReturningAdvice接口,将会在所拦截的方法执行后执行在public void afterReturning(Object arg0, Method
* arg1, Object[] arg2, Object arg3)方法中所预定的操作
*/
public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean {
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public MethodCacheAfterAdvice() {
super();
}
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3)
throws Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for (int i = 0; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if (cacheKey.startsWith(className)) {
cache.remove(cacheKey);
}
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}