1原理图
这里写图片描述

2代码实现
1)

package cn.zhou.common.web.aop;

import java.io.StringWriter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.springframework.beans.factory.annotation.Autowired;

import com.danga.MemCached.MemCachedClient;

import cn.itcast.common.web.aop.MemCachedUtil;
import cn.zhou.common.encode.Md5Pwd;
import sun.org.mozilla.javascript.internal.IdFunctionCall;

/**
 * 缓存memcached数据的切面对象
 * 
 * @author Administrator around after
 */
public class CacheInterceptor {
    @Autowired
    private MemCachedClient memCachedClient;
    @Autowired
    private Md5Pwd md5Pwd;
    // 缓存服务器时间
    private final int TIMEOUT = 3600;
    private  Integer expiry = TIMEOUT;




    // 配置环绕方法,调用service get*方法前,调用doAround方法,之后回到service get* 方法
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 去cemCachedClient查看有没有数据 key=包名+类名+方法名+参数(多个)
        String cacheKey = getCacheKey(pjp);
        // 如果缓存服务器连接不上,就去service层走数据库查数据
        if (memCachedClient.stats().isEmpty()) {
            return pjp.proceed();// 回service层,继续service层的方法
        }
        // 缓存服务器memcached没有对应键的数据
        Object object = memCachedClient.get(cacheKey);
        if (object == null) {
            // 去service层查到对应数据,并写到 memcached中
            Object proceed = pjp.proceed();
            memCachedClient.set(cacheKey, proceed,expiry);
            return proceed;
        }else {
            // 如果有数据,返回服务器数据并去controller层
            return object;// 去controller层   
        }
    }
    /**
     * aop 后置
     * 数据同步
     * 当数据有变动的时候,需要同步memcached 中 key 对应的value
     * 实现方式:删除对应的key(用户下次查询需要访问doAround方法将变化的数据写到memcached中
     * 
     */
    public void doAfter(JoinPoint jp) {
        // 包名+类名   是同一个类下的数据更新的方法,那么get *保存在缓存的数据就要更新,
        //因为都是同一个类,所以key值前面的包名+类名是相同的
        // com.danga.MemCached.MemCachedClient
        String PackageName =jp.getTarget().getClass().getName();
        PackageName=md5Pwd.encode(PackageName);
    //获取memcached的所有key
        Map<String, Object> keySet = MemCachedUtil.getKeySet(memCachedClient);
        Set<Entry<String, Object>> entrySet = keySet.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            //遍历吗每个key,
            if(entry.getKey().startsWith(PackageName)){
                memCachedClient.delete(entry.getKey());
            }
        }
    }

    /**
     * MemCached key 的命名规则 key=包名+类名+方法名+参数(多个)
     * 
     * @param pjp
     * @return
     */
    public String getCacheKey(ProceedingJoinPoint pjp) {
        StringBuilder sb = new StringBuilder();
        StringBuilder sb2 = new StringBuilder();
        // 包名
        // com.danga.MemCached.MemCachedClient
        String PackageName = pjp.getTarget().getClass().getName();

        // 方法名
        String methodName = pjp.getSignature().getName();
        if (methodName != null) {
            sb.append("." + methodName);
        }
        // 参数(多个)
        ObjectMapper om = new ObjectMapper();
        // 去掉json中value=null的key 和value
        om.setSerializationInclusion(Inclusion.NON_NULL);
        Object[] args = pjp.getArgs();

        for (Object arg : args) {
            // 对象转json,写的过程,json 是只字符串流
            StringWriter out = new StringWriter();
            try {
                om.writeValue(out, arg);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            sb.append("." + out.toString());
        }

        //memcached key 的长度有限制--255,所以为了防止超过长度,这里用md5加密==》32位长度
        //加密报名+类名===》32
        sb2.append(md5Pwd.encode(PackageName));
        //方法名+参数多个===》32
        sb2.append(md5Pwd.encode(sb.toString()));
        //返回的是64位的加密数字
        return sb2.toString();

    }


    public void setExpiry(Integer expiry) {
        this.expiry = expiry;
    }
}

2)配置

<?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"
    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">
    <!-- memcached配置 -->
    <!-- 配置客户端 -->
    <bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient">
        <!-- 设置连接池 -->
        <constructor-arg>
            <value>sockIOPool</value>
        </constructor-arg>
    </bean>
    <!--memcached连接池,帮助其实例化 -->
    <!-- factory-method="getInstance" 实例化 -->
    <!-- init-method="initialize" 初始化 -->
    <!-- destroy-method="shutDown" 销毁 -->
    <bean id="sockIOPool" class="com.danga.MemCached.SockIOPool"
        factory-method="getInstance" init-method="initialize" destroy-method="shutDown">
        <!-- 将自己的引用以构造的形式注入自己类 -->
        <constructor-arg>
            <value>sockIOPool</value>
        </constructor-arg>
        <property name="servers">
            <list>
                <!-- 配置memcached服务器的地址,如果有多个服务器,重复写value -->
                <value>192.168.44.128:11211</value>
            </list>
        </property>
        <!-- 设置权重 ,权重与上面memcached服务器地址一一对应 -->
        <property name="weights">
            <list>
                <value>1</value>
            </list>
        </property>
    </bean>
    <!-- memcached切面对象 -->
    <bean id="cacheInterceptor" class="cn.zhou.common.web.aop.CacheInterceptor">
        <property name="expiry" value="1800"></property>
    </bean>

    <!-- spring aop 配置 get* 环绕 -->
    <aop:config>
        <!--面 -->
        <aop:aspect ref="cacheInterceptor">
            <!-- 点 -->
            <!-- 切点为service层的get* 方法 -->
            <!-- *cn.zhou.core.servi 会报错误 Caused by: java.lang.IllegalArgumentException: 
                Pointcut is not well-formed,要改为* cn.zhou.c -->

                <!-- 数据查询的缓存 -->
            <aop:around method="doAround" pointcut="execution(* cn.zhou.core.service.*.*.get*(..))" />

                <!-- 数据更新,更新memcached ,方法是删掉查询的时候保存的key -->
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.update*(..))" />
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.add*(..))" />
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.delete*(..))" />
        </aop:aspect>

    </aop:config>
</beans>
posted on 2017-03-31 22:37  2637282556  阅读(224)  评论(0编辑  收藏  举报