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;
}
}