本文章摘编、转载需要注明来源 http://write.blog.csdn.net/postedit/8599512
缓存热点数据是提高访问效率的重要手段之一,下面我用encache演示下如何做service层的数据缓存
先写个方法拦截器,当然要先继承MethodInterceptor,encache的标识key我是将参数序列成json字符
可能强制部分会影响性能,建议判断null后再一次判断cache.get(key)是否为null,同步的第一人的操作已经执行完毕,那element里面就不为null,这样其他的并发用户就不需要
继续再执行put值的操作,这样做应该能比较好地降低synchronized带来的负面影响
/** * 缓存方法拦截器核心代码 * * @author shadow * @email [email protected] * @create 2012.04.28 */ public class EhCacheMethodInterceptor implements MethodInterceptor, InitializingBean { // private static final Logger log = Logger.getLogger(MethodCacheInterceptor.class); private Cache cache; public EhCacheMethodInterceptor() { super(); } 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(); String cacheKey = getCacheKey(targetName, methodName, arguments); Element element = cache.get(cacheKey); // 缓存节点不存在的情况 if (null == element) { synchronized (this) { // 这里判断是为了降低强制同步的负面影响,只需第一个操作该添加过程,后来者则跳过 if (null == cache.get(cacheKey)) element = putValueToCache(invocation, element, cacheKey); } } // 返回缓存值 return element.getValue(); } // 新增节点放到缓存区 private Element putValueToCache(MethodInvocation invocation, Element element, String cacheKey) throws Throwable { Object result = invocation.proceed(); element = new Element(cacheKey, (Serializable) result); cache.put(element); return element; } /** * * 返回具体的方法(全路径+方法名+参数值) * * @param targetName * 全路径 * @param methodName * 方法名称 * @param arguments * 参数(转换成JSON格式方便比较) * @return String */ private String getCacheKey(String targetName, String methodName, Object[] arguments) { StringBuffer buffer = new StringBuffer(""); buffer.append(targetName).append(".").append(methodName); for (Object argument : arguments) { buffer.append(".") .append(BaseSupport.ContextUtil.getJSON(argument)); } return buffer.toString(); } }
再做个同步数据的限制类,当我们执行remove等那些方法的时候会清空encache里面的相关缓存的方法数据,下次再访问的时候会重新在数据库里读取更新后的数据,到这里基本就可以了
package com.shadow.extras.cache; import java.util.List; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; /** * 缓存数据变动后让数据保持同步状态 * * @author shadow * @email [email protected] * @create 2012.04.28 */ public class EhCacheMethodAfterAdvice { protected final Logger logger = Logger.getLogger(getClass()); private CacheManager cacheManager; private Cache cacheName; /** * 原始数据发生变更时保持强一致性(刪除緩存管理器里指定key的value) * * @param joinPoint */ public void afterReturning(JoinPoint joinPoint) { // String methodName = joinPoint.getSignature().getName();// 得到执行的方法 if (cacheManager != null && cacheName != null) { // 获取缓存堆 Class<?> clazz = joinPoint.getTarget().getClass(); List<?> list = cacheName.getKeys(); for (int i = 0, len = list.size(); i < len; i++) { String key = String.valueOf(list.get(i)); if (key.startsWith(clazz.getName())) cacheName.remove(key); } } else { if (cacheManager == null) logger.error("缓存管理器不存在!"); if (cacheName == null) logger.error("缓存堆不存在!"); } } public CacheManager getCacheManager() { return cacheManager; } public void setCacheManager(CacheManager cacheManager) { this.cacheManager = cacheManager; } public Cache getCacheName() { return cacheName; } public void setCacheName(Cache cacheName) { this.cacheName = cacheName; } }
然后是在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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xmlns:ehcache="http://www.springframework.org/schema/ehcache" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/ehcache http://www.springframework.org/schema/cache/springmodules-ehcache.xsd"> <!-- 配置缓存管理器 --> <bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean> <!-- 配置一个简单的缓存工厂bean对象 --> <bean id="defaultCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <!-- 使用缓存 关联ehcache.xml中的缓存配置 --> <property name="cacheName" value="defaultCache" /> </bean> <!-- 配置一个简单的缓存工厂bean对象 --> <bean id="commonCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <!-- 使用缓存 关联ehcache.xml中的缓存配置 --> <property name="cacheName" value="commonCache" /> </bean> <!-- 配置一个缓存拦截器对象,处理具体的缓存业务 --> <bean id="ehCacheMethodInterceptor" class="com.shadow.extras.cache.EhCacheMethodInterceptor"> <property name="cache" ref="defaultCache" /> </bean> <!-- 参与缓存的切入点对象 (切入点对象,确定何时何地调用拦截器) --> <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 配置缓存切面 --> <property name="advice" ref="ehCacheMethodInterceptor" /> <!-- 配置哪些方法参与缓存策略 .表示符合任何单一字元 ### +表示符合前一个字元一次或多次 ### *表示符合前一个字元零次或多次 ### \Escape任何Regular expression使用到的符号 --> <!-- .*表示前面的前缀(包括包名) 表示print方法--> <property name="patterns"> <list> <value> com.xshadow.mvc.service\..*Service.*\.find.* </value> </list> </property> </bean> <!-- 配置一个缓存拦截器对象,处理具体的同步缓存业务 --> <bean id="ehCacheMethodAfterAdvice" class="com.shadow.extras.cache.EhCacheMethodAfterAdvice"> <property name="cacheManager" ref="defaultCacheManager" /> <property name="cacheName" ref="defaultCache" /> </bean> <aop:config> <aop:aspect id="methodCachePointCutAdviceAspect" ref="ehCacheMethodAfterAdvice"> <aop:after method="afterReturning" pointcut="execution(* com.shadow.mvc.service.*Service.modify*(..))" /> <aop:after method="afterReturning" pointcut="execution(* com.shadow.mvc.service.*Service.save*(..))" /> <aop:after method="afterReturning" pointcut="execution(* com.shadow.mvc.service.*Service.remove*(..))" /> </aop:aspect> </aop:config> </beans>
以后我们配置需要缓存的地方就直接在文件里配置就可以了,至于分布式的缓存应用有空再写