encache整合spring应用实例

本文章摘编、转载需要注明来源 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>


以后我们配置需要缓存的地方就直接在文件里配置就可以了,至于分布式的缓存应用有空再写

你可能感兴趣的:(java,spring,Web,javaee)