搭建JAVAEE Memcache 集群环境之与Spring集成(四)


1.windows本地安装memcache

    我在本地安装了memcached服务,默认端口为11211



2.编写spring集成memcache的代码


<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-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/tx 
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
	
	<bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient">
		<constructor-arg>
			<value>memCachedPool</value>		
		</constructor-arg>
	</bean>
	
	<!-- memCached连接池 -->  
	<bean id="memCachedPool" class="com.danga.MemCached.SockIOPool"  
		factory-method="getInstance" init-method="initialize" destroy-method="shutDown">  
		<constructor-arg>  
			<value>memCachedPool</value>  
		</constructor-arg>  
		<property name="servers">  
			<list>  
				<value>127.0.0.1:11211</value>  
			</list>  
		</property>  
		<property name="weights">  
			<list>  
				<value>5</value>  
			</list>  
		</property>  
		<property name="initConn" value="20"/>  
		<property name="minConn" value="10"/>  
		<property name="maxConn" value="50"/>
		<property name="maintSleep" value="30000"/> 
		<property name="nagle" value="false"/>
		<property name="socketTO" value="3000"/>
	</bean>  
	
	<!-- 配置一个缓存拦截器对象,处理具体的缓存业务 -->  
	<bean id="cacheMethodInterceptor" class="com.penguin.general.cache.memcache.CacheMethodInterceptor">  
		<property name="memCachedClient" ref="memCachedClient" />  
		<property name="expireTime" value="3600" />  
	</bean>
    
	<!-- 参与缓存的切入点对象 (切入点对象,确定何时何地调用拦截器) -->  
	<bean id="methodCachePointCut"  
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
		<!-- 配置缓存切面 -->  
		<property name="advice" ref="cacheMethodInterceptor" />  
		<property name="patterns">  
			<list>  
				<value>  
					com.penguin.general.business.service.IUserService.list
				</value>  
			</list>  
		</property>  
	</bean>
	
	<!-- 配置一个缓存拦截器对象,处理具体的同步缓存业务 -->  
	<bean id="cacheMethodAfterAdvice" class="com.penguin.general.cache.memcache.CacheMethodAfterAdvice">  
		<property name="memCachedClient" ref="memCachedClient" />  
	</bean>  
	<aop:config>  
		<aop:aspect id="methodCachePointCutAdviceAspect" ref="cacheMethodAfterAdvice">  
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.modify*(..))" />
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.update*(..))" />
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.add*(..))" /> 
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.save*(..))" />
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.del*(..))" /> 
			<aop:after method="afterReturning"  
				pointcut="execution(* com.penguin.general.business.service.*.remove*(..))" />
		</aop:aspect>  
	</aop:config> 
</beans>

这里我只是做了个实验,拦截用户模块的查询列表功能, 第一次需要查询数据库,第二次直接从缓存中获取数据

3.实现代码
package com.penguin.general.json.jackson;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;

/**
 * Jackson json 工具类
 * @author SKS
 *
 */
public class JSONUtils {

	private static final ObjectMapper objectMapper = new ObjectMapper();
	
	private JSONUtils(){}
	
	static{
		objectMapper.setSerializationInclusion(Inclusion.NON_NULL); 
		objectMapper.setSerializationInclusion(Inclusion.NON_EMPTY);
		objectMapper.configure(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES, false);
	}

	public static String toJSONString(Object obj) {

		try {
			return objectMapper.writeValueAsString(obj);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}

拦截器和aop advice
package com.penguin.general.cache.memcache;

import java.util.Calendar;

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 com.danga.MemCached.MemCachedClient;
import com.penguin.general.json.jackson.JSONUtils;

/**
 * 
 * 缓存拦截器
 * 
 * @author DiWu
 * 
 */
public class CacheMethodInterceptor implements MethodInterceptor,
		InitializingBean {

	private static final Log LOGGER = LogFactory
			.getLog(CacheMethodInterceptor.class);

	private static final int TIME_OUT = 3600;

	private Integer expireTime;

	private MemCachedClient memCachedClient;

	public void setExpireTime(Integer expireTime) {
		this.expireTime = expireTime;
	}

	public void setMemCachedClient(MemCachedClient memCachedClient) {
		this.memCachedClient = memCachedClient;
	}

	public void afterPropertiesSet() throws Exception {

	}

	public Object invoke(MethodInvocation invocation) throws Throwable {
		String className = invocation.getThis().getClass().getName();
		String methodName = invocation.getMethod().getName();
		Object[] args = invocation.getArguments();
		String cacheKey = getCacheKey(className, methodName, args);
		if (memCachedClient.stats().isEmpty()) {
			if (LOGGER.isDebugEnabled())
				LOGGER.debug("MemCached服务器尚未初始化或连接失败...");
			return invocation.proceed();
		}

		// 如果不存在缓存数据
		if (memCachedClient.get(cacheKey) == null) {
			synchronized (memCachedClient) {
				if (LOGGER.isDebugEnabled())
					LOGGER.debug("缓存" + cacheKey + "不存在或已经失效!");
				// 这里判断是为了降低强制同步的负面影响,只需第一个操作该添加过程,后来者则跳过
				if (memCachedClient.get(cacheKey) == null) { 
					Object result = invocation.proceed();
					Calendar now = Calendar.getInstance();
					now.add(Calendar.SECOND, expireTime == null?TIME_OUT:expireTime);
					memCachedClient.set(cacheKey, result, now.getTime());
				}
			}
		}
		return memCachedClient.get(cacheKey);
	}

	/**
	 * 
	 * 拼接缓存的键名称字符串
	 * 
	 * @param className
	 *            全路径类名
	 * @param methodName
	 *            方法名
	 * @param args
	 *            参数
	 * @return
	 */
	private String getCacheKey(String className, String methodName,
			Object[] args) {
		StringBuilder keyName = new StringBuilder();
		keyName.append(className).append(".").append(methodName);
		for (Object arg : args) {
			keyName.append(".").append(JSONUtils.toJSONString(arg));
		}
		return keyName.toString();
	}

}

package com.penguin.general.cache.memcache;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;

import com.danga.MemCached.MemCachedClient;

/**
 * 
 * 数据库数据更新后的aop拦截
 * 
 * @author DiWu
 *
 */
public class CacheMethodAfterAdvice {
	
	
	private static final Log LOGGER = LogFactory.getLog(CacheMethodAfterAdvice.class);
	
	private MemCachedClient memCachedClient;
	
	public void setMemCachedClient(MemCachedClient memCachedClient) {
		this.memCachedClient = memCachedClient;
	}

	public void afterReturning(JoinPoint joinPoint){
		LOGGER.info("################CacheMethodAfterAdvice##################");
		if(memCachedClient.stats().isEmpty()){
			if(LOGGER.isDebugEnabled())
				LOGGER.debug("MemCached服务器尚未初始化或连接失败...");  
		}
		
		
		Class<?> clazz = joinPoint.getTarget().getClass();
		
		// 获取memcache key集合
		List<String> keys = getCacheKeys();
		
		//清除以className开头的缓存
		for(Iterator<String> iter = keys.iterator();iter.hasNext();){
			String key = iter.next();
			if(key.startsWith(clazz.getName())){
				memCachedClient.delete(key);
			}
		}
		
	}
	
	/**
	 * 
	 * 获取缓存中所有的键
	 * 
	 * @return
	 */
	private List<String> getCacheKeys(){
		List<String> keys = new ArrayList<String>();
		Map<String, Map<String, String>> statsItems = memCachedClient.statsItems();
		Iterator<String> iter = statsItems.keySet().iterator();
		while (iter.hasNext()) {
			String server = iter.next();
			Map<String, String> items = statsItems.get(server);
			Iterator<String> it = items.keySet().iterator();

			while (it.hasNext()) {
				String itemKey = it.next();
				if (itemKey.toUpperCase().startsWith("items:".toUpperCase())
						&& itemKey.toUpperCase().endsWith(
								":number".toUpperCase())) {

					Map<String, Map<String, String>> statsCacheDump = 
							memCachedClient.statsCacheDump(new String[] { server }, Integer
									.valueOf(itemKey.split(":")[1].trim()),
									Integer.valueOf(items.get(itemKey).trim()));
					Iterator<String> cacheDumpIter = statsCacheDump.keySet().iterator();
					while(cacheDumpIter.hasNext()){
						String cacheDumpKey = cacheDumpIter.next();
						Map<String, String> cacheDumpValue = statsCacheDump.get(cacheDumpKey);
						
						for(Iterator<String> itemKeys=cacheDumpValue.keySet().iterator();itemKeys.hasNext();){
							String key = itemKeys.next();
							try {
								keys.add(URLDecoder.decode(key, "utf-8"));
							} catch (UnsupportedEncodingException e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
			
		}
		return keys;
	}
	
	
}



    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值