利用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: 具體測試方法就是以上的步驟了,理論知識感謝開篇引用的博客兩位大神,我正在研究不用數據庫表實現的方法,實現之后會再貼出來的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM