<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="java.io.tmpdir" /> <defaultCache maxElementsInMemory="100" eternal="false" overflowToDisk="true" timeToIdleSeconds="30" timeToLiveSeconds="300" /> <cache name="DEFAULT_CACHE" maxElementsInMemory="50000" eternal="false" overflowToDisk="true" timeToIdleSeconds="60" timeToLiveSeconds="300" /> </ehcache>
各配置参数的含义:
maxElementsInMemory:缓存中允许创建的最大对象数
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
overflowToDisk:内存不足时,是否启用磁盘缓存。
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻 留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。LRU和FIFO算法这里就不做介绍。LFU算法直接淘汰使用比较少的对象,在内存保留的都是一些经常访问的对象。对于大部分网站项目,该算法比较适用。
如果应用需要配置多个不同命名并采用不同参数的Cache,可以相应修改配置文件,增加需要的Cache配置即可。
Spring整合ehcache
首先,在CLASSPATH下面放置ehcache.xml配置文件。在Spring的配置文件中先添加如下defaultCacheManager配置:
<bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean>
配置ehcache.xml 中定义的cache
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <property name="cacheName"> <value>DEFAULT_CACHE</value> </property> </bean>
接下来,写一个实现org.aopalliance.intercept.MethodInterceptor接口的拦截器类。有了拦截器就可以有选择性的配置想要缓存的 bean 方法。如果被调用的方法配置为可缓存,拦截器将为该方法生成 cache key 并检查该方法返回的结果是否已缓存。如果已缓存,就返回缓存的结果,否则再次执行被拦截的方法,并缓存结果供下次调用。具体代码如下:
import java.io.Serializable; import java.lang.reflect.Field; import java.util.Collection; import java.util.Map; import java.util.Set; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; 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 MethodCacheInterceptor() { super(); } /** * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在就返回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=null; boolean hasCache = true; if(invocation.getThis() instanceof IDaoOperation){ IDaoOperation dao = (IDaoOperation)invocation.getThis(); hasCache = dao.getCache(); } else{ //hasCache 取对象的hasCache属性 Object obj = invocation.getThis(); try{ Field field = obj.getClass().getDeclaredField("hasCache"); field.setAccessible(true); Boolean boc = (Boolean)field.get(obj); hasCache = boc; }catch(Exception e){} } //告警配置不使用缓存 hasCache = targetName.startsWith("com.xxxx.xxxx")?false:hasCache; // try{ // hasCache = (Boolean) ReflectUtil.getter(invocation.getThis(), "hasCache"); // }catch(Exception e){e.printStackTrace();} // boolean hasNotCache = methodName.endsWith("NotCache"); logger.debug("Find object from cache is " + cache.getName()); // methodName = hasNotCache?methodName.substring(0,methodName.length()-"NotCache".length()):methodName; String cacheKey = getCacheKey(targetName, methodName, arguments); Element element = null; if(hasCache){ element = cache.get(cacheKey); } else{ cache.remove(cacheKey); } //开发时方式缓存读取 if("false".equalsIgnoreCase(ConfigPropertiesUtil.getValue("SYSTEM_HASCACHE"))){ element=null; } result = element!=null?element.getValue():result; if (element == null) { logger.debug("Hold up method , Get method result and create cache........!"); result = invocation.proceed(); if(result!=null){ cache.remove(cacheKey); element = new Element(cacheKey, (Serializable) result); cache.put(element); } } return result; } /** * 将对象转换成string,处理数组、List、MAP的处理 * @param value * @return */ private static String objectToString(Object value){ if(value==null){return null;} StringBuffer sb = new StringBuffer(); Object[] valueTemps = null; if(value instanceof Object[]){ valueTemps = (Object[])value; } else if(value instanceof Collection){ valueTemps = ((Collection)value).toArray(); } if(valueTemps!=null){ sb.append("["); for(Object obj : valueTemps){ sb.append(objectToString(obj)+","); } sb.append("]"); return sb.toString().hashCode()+""; } else if(value instanceof Map){ sb.append("["); Set<Map.Entry> entitySet = ((Map)value).entrySet(); for(Map.Entry entity:entitySet){ Object key = entity.getKey(); Object kv = entity.getValue(); sb.append(objectToString(key)+"="+objectToString(kv)+","); } sb.append("]"); return sb.toString().hashCode()+""; } else{ return value.hashCode()+""; } } /** * 获得cache key的方法,cache key是Cache中一个Element的唯一标识 * cache key包括 包名+类名+方法名,如com.co.cache.service.UserServiceImpl.getAllUser */ 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)) { sb.append("{"); for (int i = 0; i < arguments.length; i++) { sb.append(objectToString(arguments[i])+","); sb.append(";"); } sb.append("}"); } return sb.toString(); } /** * implement InitializingBean,检查cache是否为空 */ public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it."); } }