• 利用Spring AOP 更新memcached 缓存策略的实现(一) 东师理想


    原创文章,请尊重作者的辛勤劳动,转载请注明!

    本人参考文档:http://blog.csdn.net/ajun_studio/article/details/7343781

    memcached批量删除解决方案:http://tech.ddvip.com/2008-10/122405933178234.html

    (2013-4-10续 本人实现了不用数据库临时表的方法,见 利用Spring AOP 更新memcached 缓存策略的实现(二))

    这里实现的是“LogDB方案”,及用数据库的一个表记录memcached的实现

    实现思路:

    1. 查询数据时,先查看memcached中是否存在要查找结果,如果存在读取memcached,不存在,从数据库中读取,并存入memcached中

    2. 以一个list<bean>为例,如果修改其中一个person的数据,则删除相关list信息,修改学生数据,再重新存储memcached数据

    3. 利用spring-aop和注解实现以上逻辑,做到对业务逻辑代码的解耦

    具体实现步骤如下:

    1. 配置spring-aop,因为我这里还用的是spring-mvc框架,所以引用比较多,spring版本为spring-framework-3.2.1.RELEASE

    <?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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:cache="http://www.springframework.org/schema/cache"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
               http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
                http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
                http://www.springframework.org/schema/mvc
                 http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
                
        
        <!-- 引入jdbc配置文件 -->
        <context:property-placeholder location="classpath*:jdbc.properties"/>
        <!--创建jdbc数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <property name="driverClass" value="${driverClass}"/>
            <property name="jdbcUrl" value="${jdbcUrl}"/>
            <property name="user" value="${user}"/>
            <property name="password" value="${password}"/>
            <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。-->
            <property name="initialPoolSize" value="${initialPoolSize}"/>
            <!--连接池中保留的最小连接数。-->
            <property name="minPoolSize" value="${minPoolSize}"/>    
            <!--连接池中保留的最大连接数。-->
            <property name="maxPoolSize" value="${maxPoolSize}"/>
            <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。-->
            <property name="maxIdleTime" value="${maxIdleTime}"/>    
            <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。-->
            <property name="acquireIncrement" value="${acquireIncrement}"/>    
            <!--每60秒检查所有连接池中的空闲连接。-->
            <property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/>
        </bean>
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
             <property name="dataSource" ref="dataSource"/>
        </bean>
        <!-- 可通过注解控制事务 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
        
        <!-- 自动扫描bean,把作了注解的类转换为bean -->
        <context:component-scan base-package="com.dsideal" />
        <!-- 加载组装所以配置文件 context:component-scan注册后可以省略当前配置
        <context:annotation-config />     
        -->
        
        <!-- 通过注解,把URL映射到Controller上,该标签默认注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter -->
        <mvc:annotation-driven />
        
        <!-- 启用AOP注解  -->
        <aop:aspectj-autoproxy/>
        
        <!-- 视图解析器 InternalResourceViewResolver:支持jsp和jstl解析器 -->
        <bean id="viewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 使用JSP页面进行输出 -->
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
            <!-- 指定了表示层的前缀 -->
            <property name="prefix" value="/WEB-INF/bizroot/" />
            <!-- 指定了表示层的后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
        
         <!-- 处理文件上传处理  maxUploadSize:最大上传限制 uploadTempDir:上传临时路径,文件上传完成后,临时目录中的临时文件会被自动清除 -->
        <bean id="multipartResolver"     
              class="org.springframework.web.multipart.commons.CommonsMultipartResolver"     
              p:defaultEncoding="UTF-8" 
              p:maxUploadSize="5242880"
              />   
        
        
        <!-- Session,角色权限的拦截器 黄海添加于2013-03-15-->
        <mvc:interceptors>
                <bean class="com.dsideal.interceptor.SystemInterceptor" />
        </mvc:interceptors>
    </beans>

    2. 下载memcached的jar包,相关方法请参考本人之前的博客:这里

    3. 编写两个注解,分别为@Cache和@Flush

    package com.dsideal.common;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 用于查找的时候,放置缓存信息
     * @author Administrator
     *
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface Cache {
        //key的前缀
        String prefix();
        //缓存有效期 1000*60*60*2=2小时
        long expiration() default 1000*60*60*2;
    }
    package com.dsideal.common;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 用于删除缓存
     * @author Administrator
     *
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface Flush {
        //key的前缀
        String prefix();
    }

    4. 数据库端创建临时表存储memecached的key值,prefix:包名前缀;cache_key:存入memcached的key(包名前缀+参数类型+参数值转MD5)

    注:这里cache_key这样存储的原因是防止查找key时发生混乱,比如两个参数,1个为年级,1个为班级,两个如果有一个为空的时候,就会产生key值重复的情况;

    转存MD5是怕参数过多,key过长

    CREATE TABLE `t_cache_log` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `prefix` varchar(20) DEFAULT NULL,
      `cache_key` varchar(300) DEFAULT NULL,
      `add_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8;

    5. memcached的配置类为

    package com.dsideal.sys.memcached;
    
    import java.util.Date;
    
    import com.danga.MemCached.MemCachedClient;
    import com.danga.MemCached.SockIOPool;
    
    public class MemcacheBlog {
        // 创建全局的唯一实例
    
    
        static MemCachedClient memCachedClient=null;
    
        // 设置与缓存服务器的连接池
        static{  
            //memcached服务器端IP和端口
            String[] servers = {"192.168.100.102:11211"};    
            SockIOPool pool = SockIOPool.getInstance();    
            pool.setServers(servers);    
            pool.setFailover(true);    
            // 设置初始连接数、最小和最大连接数以及最大处理时间   
            /*    pool.setInitConn(5); 
            pool.setMinConn(5); 
            pool.setMaxConn(250); 
            pool.setMaxIdle(1000 * 60 * 60 * 6); */  
            pool.setInitConn(10);    
            pool.setMinConn(5);    
            pool.setMaxConn(250);    
            pool.setMaintSleep(30);  // 设置主线程的睡眠时间   
            // 设置TCP的参数,连接超时等   
            pool.setNagle(false);    
            pool.setSocketTO(3000);    
            pool.setAliveCheck(true);   
    
            pool.initialize();    
    
            memCachedClient = new MemCachedClient();      
            memCachedClient.setPrimitiveAsString(true); 
        }  
    
        /**
         * <p>功能:
         * get:获取数据的方法
         * get_multi:一次取得多条数据;getmulti可以非同步地同时取得多个键值, 其速度要比循环调用get快数十倍
         * </p>
         * @author 周枫
         * @date 2013-4-3
         * @param 
         * @return Object
         */
        public static Object get(String key)  
        {  
            return memCachedClient.get(key);  
        }  
        /**
         * <p>功能:
         * add:仅当存储空间中不存在键相同的数据时才保存
         * replace:仅当存储空间中存在键相同的数据时才保存
         * set:与add和replace不同,无论何时都保存</p>
         * @author 周枫
         * @date 2013-4-3
         * @param 
         * @return Object
         */
        public static boolean set(String key,Object o)  
        {  
            return memCachedClient.set(key, o);       
        }  
        public static boolean set(String key,Object o,Date ExpireTime)  
        {         
            return memCachedClient.set(key, o, ExpireTime);  
        }  
        public static boolean exists(String key)  
        {  
            return memCachedClient.keyExists(key);  
        }  
        public static boolean delete(String key)  
        {  
            return memCachedClient.delete(key);  
        }  
    
    }

    6. spring-aop实现类

    package com.dsideal.sys.action;
    
    import java.lang.reflect.Method;  
    import java.util.ArrayList;
    import java.util.Date;  
    import java.util.List;  
    
    import javax.annotation.Resource;  
    
    import org.aspectj.lang.ProceedingJoinPoint;  
    import org.aspectj.lang.Signature;  
    import org.aspectj.lang.annotation.Around;  
    import org.aspectj.lang.annotation.Aspect;  
    import org.aspectj.lang.annotation.Pointcut;  
    import org.aspectj.lang.reflect.MethodSignature;  
    import org.springframework.stereotype.Component;  
    
    import com.alibaba.fastjson.JSON;
    
    import com.dsideal.common.Cache;
    import com.dsideal.common.Flush;
    import com.dsideal.sys.bean.CacheLog;
    import com.dsideal.sys.bean.SysLoginPersonBean;
    import com.dsideal.sys.memcached.MemcacheBlog;
    import com.dsideal.sys.service.ICacheLogService;
    import com.dsideal.common.MD5;
    
    @Aspect  
    @Component 
    public class CacheAopBlog {
    
        //  @Pointcut("execution(* add*(..)) || (execution(* del*(..))) || (execution(* get*(..)))")  
        //* com.dsideal.sys.service.impl.*.getMemcache*(..)
        @Pointcut("execution (* com.dsideal.sys.service.impl.*.memcache*(..))")  
        public void pointcut(){}  
    
        //方法执行前调用  
        //@Before("pointcut()")  
        public void before() {  
            System.out.println("before");  //2
        }  
    
        @Resource
        private ICacheLogService cacheLogService;  
    
    
    
        //方法执行的前后调用   
        @Around("pointcut()")  
        //ProceedingJoinPoint 目标类连接点对象
        public Object doAround(ProceedingJoinPoint call) throws Throwable{  
            //返回最终结果
            Object result = null;  
            Method[] methods = call.getTarget().getClass().getDeclaredMethods();    
            Signature signature = call.getSignature();  
            MethodSignature methodSignature = (MethodSignature) signature;    
            Method method = methodSignature.getMethod();  
            for(Method m:methods){
                //循环方法,找匹配的方法进行执行  
                if(m.getName().equals(method.getName())){
                    //增加
                    if(m.isAnnotationPresent(Cache.class)){  
                        Cache cache = m.getAnnotation(Cache.class);
                        Object tempType = m.getGenericReturnType();
                        //System.out.println(m.get);
                        //如果memcached中存在
                        if(cache!=null){  
                            //获取方法名+参数类型+参数值 转 MD5
                            String tempKey = this.getKey(method, call.getArgs());  
                            //获取注解前缀,这里为 sys,实际使用是为各个业务包名
                            String prefix = cache.prefix();  
                            //存入memcached的最终key值
                            String key = prefix+"_"+tempKey;  
                            result =MemcacheBlog.get(key);  
                            if(null == result){  
                                try {  
                                    //执行aop拦截的方法
                                    result = call.proceed();  
                                    //获取注解配置memcached死亡时间
                                    long expiration = cache.expiration();
                                    //1000*60*60*2==2小时过期  
                                    Date d=new Date();  
                                    //memcached死亡时间
                                    d=new Date(d.getTime()+expiration);
                                    //利用fastjson序列化list<bean>存入memcached中
                                    //具体fastjson使用方法请参考:http://www.cnblogs.com/cczhoufeng/archive/2013/04/03/2997871.html
                                    MemcacheBlog.set(key, JSON.toJSONString(result), d);
                                    //将key存入数据库  
                                    CacheLog log = new CacheLog();  
                                    log.setPrefix(prefix);  
                                    log.setCache_key(key);  
                                    //存入临时表中
                                    this.cacheLogService.add(log);  
                                } catch (Throwable e) {  
                                    e.printStackTrace();  
                                }  
                            }  else {
                                //如果memcached中存在结果,需要将result反序列化后返回结果
                                String memresult = result.toString();
                                //反序列化
                                List<SysLoginPersonBean> list = JSON.parseArray(memresult, SysLoginPersonBean.class);
                                result = list;
                                //这里是利用fastjson反序列化输出的方法
                                //String memresult = result.toString();
                                //List<SysLoginPersonBean> list =  JSON.parseArray(memresult, SysLoginPersonBean.class);
                                //for (int i = 0; i < list.size(); i++) {
                                //    System.out.println(list.get(i).getReal_name());
                                //}
                            }
    
                        }  
                    } else  if(m.isAnnotationPresent(Flush.class)){  
                        Flush flush = m.getAnnotation(Flush.class);  
                        if(flush!=null){  
                            result = call.proceed();
                            String prefix = flush.prefix();  
                            List<CacheLog> logs = cacheLogService.findListByPrefix(prefix);  
                            if(logs!=null && !logs.isEmpty()){  
                                //删除数据库  
                                int rows =  cacheLogService.deleteByPrefix(prefix);  
                                if(rows>0){  
                                    for(CacheLog log :logs){  
                                        if(log!=null){  
                                            String key = log.getCache_key();  
                                            MemcacheBlog.delete(key);//删除缓存  
                                        }  
                                    }  
                                }  
                            }  
                        }  
                    }else{  
                        try {  
                            result = call.proceed();  
                        } catch (Throwable e) {  
                            e.printStackTrace();  
                        }  
                    }  
                    break;  
                }  
            }
    
            return result;
        }
    
        /** 
         * 组装key值 
         * @param method 
         * @param args 
         * @return 
         */  
        private String getKey(Method method, Object [] args){  
            StringBuffer sb = new StringBuffer();   
            //获取方法名
            String methodName = method.getName();
            //获取参数类型
            Object[] classTemps = method.getParameterTypes();
            //存入方法名
            sb.append(methodName);
    
            for (int i = 0; i < args.length; i++) {
                sb.append(classTemps[i]+"&");
                if (null == args[i]) {
                    sb.append("null");
                } else if ("".equals(args[i])) {
                    sb.append("*");
                } else {
                    sb.append(args[i]);
                }
            }
            return MD5.getMD5(sb.toString());  
    
        }  
    }

    7. MD5的实现类

    package com.dsideal.common;
    
    import java.security.MessageDigest;
    
    public class MD5 {
        public static String getMD5(String sourceStr) 
        { 
            char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 
            try 
            { 
                byte[] strTemp = sourceStr.getBytes(); 
                MessageDigest mdTemp = MessageDigest.getInstance("MD5"); 
                mdTemp.update(strTemp); 
                byte[] md = mdTemp.digest(); 
                int j = md.length; 
                char str[] = new char[j * 2]; 
                int k = 0; 
                for (int i = 0; i < j; i++) 
                { 
                    byte byte0 = md[i]; 
                    str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移 
    
                    str[k++] = hexDigits[byte0 & 0xf];  // 取字节中低 4 位的数字转换 
                } 
                return new String(str); 
            } 
            catch (Exception e) 
            { 
                return null; 
            }  
    
        } 
    
    }

    8. 我做列子用的表,mysql数据库

    /*
    Navicat MySQL Data Transfer
    
    Source Server         : localhost
    Source Server Version : 50529
    Source Host           : localhost:3306
    Source Database       : lxyy_db
    
    Target Server Type    : MYSQL
    Target Server Version : 50529
    File Encoding         : 65001
    
    Date: 2013-04-09 10:34:24
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `t_sys_loginperson`
    -- ----------------------------
    DROP TABLE IF EXISTS `t_sys_loginperson`;
    CREATE TABLE `t_sys_loginperson` (
      `person_id` char(36) NOT NULL,
      `login_name` varchar(128) NOT NULL,
      `login_pwd` varchar(128) NOT NULL,
      `b_use` int(11) NOT NULL,
      `identity_id` int(11) DEFAULT NULL,
      `bureau_id` int(11) DEFAULT NULL,
      `real_name` varchar(128) DEFAULT NULL,
      PRIMARY KEY (`person_id`),
      UNIQUE KEY `ix_t_sys_loginperson` (`login_name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of t_sys_loginperson
    -- ----------------------------
    INSERT INTO `t_sys_loginperson` VALUES ('0E04DE60-7264-4FE7-9A6C-5AB843B603CC', 'czls', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中历史管理员1');
    INSERT INTO `t_sys_loginperson` VALUES ('0E324A5B-981B-40A6-8AFF-1B3D55E486E6', 'gzhx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中化学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('2383E58E-2488-4C1F-A262-3BE64C87A12A', 'gzdl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中地理管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('2D53739E-B2EF-4CC2-BA93-8355EF30528C', 'czyinyue', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中音乐管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('3C9B016C-5E30-46B0-A401-A434108D6725', 'czsw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中生物管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('3FDC5489-6B70-11E2-B11E-00FF2D04A858', 'xxsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小学数学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('524C0D25-CC64-4C34-A168-F8A6FB2FA4AC', 'czwl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中物理管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('9113D74A-0292-4EBB-8C52-77E71F5AFC16', 'xxyinyue', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小学音乐管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('9210EE35-51B3-412B-BFF9-9CDB684A3B2F', 'czhx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中化学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('974E3FB8-04D0-42F2-9B8A-5D942C2B954F', 'xxyingyu', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小学英语管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('A9609550-7B82-4780-BC5D-B172BCA32230', 'xxkx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小学科学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('A9AAEB9E-ADD4-425A-8FCF-241E9A9C12EC', 'gzwl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中物理管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('D26A1BFC-7996-408B-8157-0CFB77A9D427', 'czsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中数学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('D43EA9DD-1EDA-437B-A012-CF35238B750E', 'gzsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中数学管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('D6EC9FBB-1C3C-40EC-8D18-7CD48A7B9CA3', 'gzsw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中生物管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('E49F1620-F6AA-42BB-96A8-198F642FCE59', 'xxyw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小学语文管理员');
    INSERT INTO `t_sys_loginperson` VALUES ('F38A7B0C-A292-4690-8D2D-EC7CC5E4F069', 'czdl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中地理管理员');

    9. 生成bean

    package com.dsideal.sys.bean;
    
    @SuppressWarnings("serial")
    public class SysLoginPersonBean {
        private static final long serialVersionUID = 7517080513591583073L;
        private String person_id;
        private String login_name;
        private String login_pwd;
        private int b_use;
        private int identity_id;
        private int bureau_id;
        private String real_name;
        public String getPerson_id() {
            return person_id;
        }
        public void setPerson_id(String person_id) {
            this.person_id = person_id;
        }
        public String getLogin_name() {
            return login_name;
        }
        public void setLogin_name(String login_name) {
            this.login_name = login_name;
        }
        public String getLogin_pwd() {
            return login_pwd;
        }
        public void setLogin_pwd(String login_pwd) {
            this.login_pwd = login_pwd;
        }
        public int getB_use() {
            return b_use;
        }
        public void setB_use(int b_use) {
            this.b_use = b_use;
        }
        public int getIdentity_id() {
            return identity_id;
        }
        public void setIdentity_id(int identity_id) {
            this.identity_id = identity_id;
        }
        public int getBureau_id() {
            return bureau_id;
        }
        public void setBureau_id(int bureau_id) {
            this.bureau_id = bureau_id;
        }
        public String getReal_name() {
            return real_name;
        }
        public void setReal_name(String real_name) {
            this.real_name = real_name;
        }
    
        public SysLoginPersonBean() {
    
        }
    
        public SysLoginPersonBean(String person_id, String login_name, 
                String login_pwd, int b_use, int identity_id, int bureau_id, String real_name) {
            this.person_id = person_id;
            this.login_name = login_name;
            this.login_pwd = login_pwd;
            this.b_use = b_use;
            this.identity_id = identity_id;
            this.bureau_id = bureau_id;
            this.real_name = real_name;
        }
    }

    10. 接口文件 ICacheLogService

    package com.dsideal.sys.service;
    
    import java.util.List;
    
    import com.dsideal.sys.bean.CacheLog;
    import com.dsideal.sys.bean.SysLoginPersonBean;
    
    public interface ICacheLogService {
    
        /**
         * <p>功能:增加memcached数据文件到临时表中</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return void
         */
        public void add(CacheLog log);
    
        /**
         * <p>功能:查询以prefix为前缀的所有key值,在更新删除时使用此方法</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return List<CacheLog>
         */
        public List<CacheLog> findListByPrefix(String prefix);
    
        /**
         * <p>功能:删除操作时,aop拦截</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return void
         */
        public void memcacheDeleteByPrefix();
    
        /**
         * <p>功能:删除临时表记录的数据</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return int
         */
        public int deleteByPrefix(String prefix);
    
        /**
         * <p>功能:查找例子,查找所有人员数据,后面的person_id没有使用,只是为了测试key值的生成策略</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return List<SysLoginPersonBean>
         */
        public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id);
    
        /**
         * <p>功能:测试方法,可以忽略</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return List<CacheLog>
         */
        public List<CacheLog> memcacheCacheLogFindAll();
    
        /**
         * <p>功能:修改人员</p>
         * @author 周枫
         * @date 2013-4-9
         * @param 
         * @return int
         */
        public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name);
    
    
    }

    11. 接口实现类,"sys"为业务包名,为key的前缀,expiration:自定义memcached死亡时间

    package com.dsideal.sys.service.impl;
    
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.dsideal.common.Cache;
    import com.dsideal.common.Flush;
    import com.dsideal.sys.bean.CacheLog;
    import com.dsideal.sys.bean.SysLoginPersonBean;
    import com.dsideal.sys.dao.CacheLogDao;
    import com.dsideal.sys.service.ICacheLogService;
    
    @Service
    public class CacheLogServiceImpl implements ICacheLogService {
    
        @Resource
        private CacheLogDao dao;
    
        @Override
        public void add(CacheLog log) {
            dao.add(log);
        }
    
        @Override
        public List<CacheLog> findListByPrefix(String prefix) {
            // TODO Auto-generated method stub
            return dao.findListByPrefix(prefix);
        }
    
        @Override
        @Flush(prefix="sys")
        public void memcacheDeleteByPrefix() {
            // TODO Auto-generated method stub
        }
    
        @Override
        public int deleteByPrefix(String prefix) {
            // TODO Auto-generated method stub
            return dao.deleteByPrefix(prefix);
        }
    
        @Override
        @Cache(prefix="sys",expiration=1000*60*60*2)
        public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id) {
            // TODO Auto-generated method stub
            return dao.findAll(b_use);
        }
    
        @Override
        @Cache(prefix="sys",expiration=1000*60*60*2)
        public List<CacheLog> memcacheCacheLogFindAll() {
            // TODO Auto-generated method stub
            return dao.findCacheLogAll();
        }
    
        @Override
        @Flush(prefix="sys")
        public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name) {
            return dao.updateSysLoginPersonBean(person_id,real_name);
    
        }
    
    }

    12. spring的jdbctemplate实现dao层

    package com.dsideal.sys.dao;
    
    import org.springframework.dao.DataAccessException;
    import org.springframework.stereotype.Repository;
    
    import java.util.Date;
    import java.util.List;
    
    import com.dsideal.common.BaseDao;
    import com.dsideal.common.Utils.RSMapper;
    import com.dsideal.sys.bean.CacheLog;
    import com.dsideal.sys.bean.SysLoginPersonBean;
    
    
    @Repository
    public class CacheLogDao extends BaseDao {
        public void add(CacheLog log) {
            try {
                String sql = "INSERT INTO t_cache_log(prefix,cache_key,add_time) VALUES (?,?,?)";
                int result = 0;
                Date now = new Date();
                result = this.jdbcTemplate.update(sql, log.getPrefix(),log.getCache_key(),now);
                System.out.println("增加成功");
            } catch (DataAccessException e) {
                e.printStackTrace();
                System.out.println("增加报错");
            }
        }
    
        public int deleteByPrefix(String prefix) {
            try {
                String sql = "DELETE FROM t_cache_log WHERE prefix = ?";
                int result = 0;
                result = this.jdbcTemplate.update(sql, prefix);
                return result;
            } catch (DataAccessException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        public List<CacheLog> findListByPrefix(String prefix) {
            try {
                String sql = "SELECT * FROM t_cache_log WHERE prefix = ?";
                return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class, prefix);
            } catch (DataAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public List<SysLoginPersonBean> findAll(int b_use) {
            try {
                String sql = "SELECT * FROM t_sys_loginperson WHERE b_use = ?";
                return RSMapper.queryList(jdbcTemplate, sql, SysLoginPersonBean.class,b_use);
            } catch (DataAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public List<CacheLog> findCacheLogAll() {
            try {
                String sql = "SELECT * FROM t_cache_log";
                return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class);
            } catch (DataAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public int updateSysLoginPersonBean(String person_id,String real_name) {
            int result = 0;
            try {
                String sql = "UPDATE t_sys_loginperson SET real_name = ? WHERE person_id = ?";
    
                return this.jdbcTemplate.update(sql, real_name,person_id);
            } catch (DataAccessException e) {
                e.printStackTrace();
                System.out.println("修改时报错");
            }
            return result;
        }
    }

    13.  Service测试类

    package com.dsideal.sys.test;
    
    import java.util.List;
    
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.dsideal.sys.bean.CacheLog;
    import com.dsideal.sys.bean.SysLoginPersonBean;
    import com.dsideal.sys.service.ICacheLogService;
    import com.dsideal.sys.service.impl.CacheLogServiceImpl;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    //指定Spring的配置文件 /为classpath下
    @ContextConfiguration(locations = {"/spring-mvc.xml"}) 
    public class CacheLogServiceTest {
    
        @Autowired
        private ICacheLogService impl;
    
        @Before //在每个测试用例方法之前都会执行  
        public void init(){  
    
        }  
    
        @After //在每个测试用例执行完之后执行  
        public void destory(){  
    
        }  
    
        @Test
        public void add() {
            CacheLog log = new CacheLog();
            impl.add(log);
        }
    
        @Test
        public void findAll() {
            int b_use = 1;
            String person_id = "";
            List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id);
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i).getReal_name());
            }
    
        }
    
        @Test
        public void deleteByPrefix() {
            System.out.println("1");
            impl.memcacheDeleteByPrefix();
            System.out.println("删除成功");
        }
    
        @Test
        public void findCacheLogAll() {
            List<CacheLog> list = impl.memcacheCacheLogFindAll();
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i).getCache_key());
            }
    
        }
    
        @Test
        public void updateToDeleteToFind() {
            String prefix = "sys";
            String person_id1 = "0E04DE60-7264-4FE7-9A6C-5AB843B603CC";
            String person_id2 = "";
            String real_name = "初中历史管理员1";
            int result = impl.memcacheupdateSysLoginPersonBean(prefix, person_id1, real_name);
            if (result > 0) {
                System.out.println("修改成功");
            }
    
            int b_use = 1;
            List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id2);
            System.out.println("查询成功");
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i).getReal_name());
            }
    
        }
    }

    具体测试方法为:

    1. 当memcached服务器端和临时表均为空时,测试类中执行 findAll() 方法,进入aop拦截"doAround"方法,添加当前数据list<SysLoginPersonBean>进入memcached中,并在临时表记录当前key值

    2. 再次执行"findAll()"方法,跟踪测试,查找结果将从memcached中查找结果并反序列化后返回结果

    3. 当修改人员中一人时,执行"updateToDeleteToFind()"方法,这里的测试方法,我是为了测试而写,所以先修改了人员的数据,再次执行了一遍查询,查询同第一条,可以发现,list<SysLoginPersonBean>中的值已经修改,因为我在执行"memcacheupdateSysLoginPersonBean()"方法时,加入了@Flush注解,再修改的时候,aop拦截此方法,执行删除操作,再重新存入数据到memcached和临时表

    PS: 具体测试方法就是以上的步骤了,理论知识感谢开篇引用的博客两位大神,我正在研究不用数据库表实现的方法,实现之后会再贴出来的

  • 相关阅读:
    如何控制input框!
    火车头采集器破解版
    记Angular与Django REST框架的一次合作(2):前端组件化——Angular
    拉勾网一些“震惊”的结论
    一个知乎重度用户眼中的知乎
    anthelion编译
    搜索引擎爬虫蜘蛛的USERAGENT大全
    Netty系列之Netty高性能之道
    python正则表达式
    Cookie的使用,详解,获取,无法互通、客户端获取Cookie、深入解析cookie
  • 原文地址:https://www.cnblogs.com/cczhoufeng/p/3009578.html
Copyright © 2020-2023  润新知