自定義注解&Spring AOP實現(xiàn)為原程序加入Redis緩存支持(可重用)

應(yīng)用場景##

  • 數(shù)據(jù)訪問采用ORM方式(Hibernate) 直接訪問數(shù)據(jù)庫喷舀,在訪問量小、并發(fā)性小废士、數(shù)據(jù)量小時叫潦,可正常訪問,反之則服務(wù)響應(yīng)能力低官硝。
  • 福利彩蛋

目標(biāo)&要解決的問題##

  • 自定義注解&Spring AOP為項目加入Redis緩存依賴提高應(yīng)用程序的響應(yīng)能力(可重用)

項目擴充承接于http://www.reibang.com/p/25039d901ac2

難點##

設(shè)置緩存的失效策略矗蕊,緩存數(shù)據(jù)的Struct選取,切面(Aspect)的編寫

方法&擴充步驟##

1.擴充build.gradle 腳本文件

    //https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis 項目添加redis支持
    compile group: 'org.springframework.data', name: 'spring-data-redis', version: '1.4.1.RELEASE'
    // https://mvnrepository.com/artifact/redis.clients/jedis redis 基于java的Redis客戶端調(diào)用實現(xiàn)
    compile group: 'redis.clients', name: 'jedis', version: '2.6.1'
    // https://mvnrepository.com/artifact/com.alibaba/fastjson
    // 采用阿里巴巴fastjson 進行對象&json字符串的序列化與反序列化
    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.21'

2.擴充Spring 配置文件氢架,添加Redis相關(guān)Java Bean 到Ioc容器中
為了符合開閉原則傻咖,重新創(chuàng)建Spring 配置文件 spring-redis.xml

<?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"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="100" />
       <!-- <property name="max" value="${redis.maxActive}" />
        <property name="maxWait" value="${redis.maxWait}" />-->
        <property name="testOnBorrow" value="true" />
    </bean>

    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          p:host-name="127.0.0.1" p:port="6379" p:password="ls"  p:pool-config-ref="jedisPoolConfig"/>

    <bean id="redisTemplateForString" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory"   ref="connectionFactory" />
    </bean>

</beans>

3.自定義兩個注解

  • RedisCahe: 標(biāo)識緩存 注解
  • RedisEvit: 標(biāo)識緩存清除 注解

代碼如下:
RedisCahe.java

package com.fxmms.common.rediscache.redisannotation;

import java.lang.annotation.*;

/**
 * Created by mark on 16/11/29.
 * @usage  緩存注解類
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisCache {
    Class type();//被代理類的全類名,在之后會做為redis hash 的key
}

RedisEvit.java

package com.fxmms.common.rediscache.redisannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by mark on 16/11/29.
 * @usage 清除過期緩存注解达箍,放置于update delete insert 類型邏輯之上
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisEvict {
    Class type();
}

4.RedisCacheAspect.java 切面程序

package com.fxmms.common.rediscache.redisaspect;

import com.fxmms.common.rediscache.redisannotation.RedisCache;
import com.fxmms.common.rediscache.redisannotation.RedisEvict;
import com.fxmms.common.util.FastJsonUtil;
import com.fxmms.common.util.JsonUtil;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
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.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;

/**
 * Created by mark on 16/11/29.
 */
@Aspect
@Component
@SuppressWarnings(value = {"rawtypes", "unchecked"})
public class RedisCacheAspect {

    private static final Logger logger = Logger.getLogger(RedisCacheAspect.class);
    /**
     * 分隔符 生成key 格式為 類全類名|方法名|參數(shù)所屬類全類名
     **/
    private static final String DELIMITER = "|";
    /**
     * spring-redis.xml配置連接池没龙、連接工廠铺厨、Redis模板
     **/
    @Autowired
    @Qualifier("redisTemplateForString")
    StringRedisTemplate srt;

    /**
     * Service層切點 使用到了我們定義的 RedisCache 作為切點表達式缎玫。
     * 而且我們可以看出此表達式基于 annotation硬纤。
     * 并且用于內(nèi)建屬性為查詢的方法之上
     */
    @Pointcut("@annotation(com.fxmms.common.rediscache.redisannotation.RedisCache)")
    public void redisCacheAspect() {
    }

    /**
     * Service層切點 使用到了我們定義的 RedisEvict 作為切點表達式。
     * 而且我們可以看出此表達式是基于 annotation 的赃磨。
     * 并且用于內(nèi)建屬性為非查詢的方法之上筝家,用于更新表
     */
    @Pointcut("@annotation(com.fxmms.common.rediscache.redisannotation.RedisEvict)")
    public void redisCacheEvict() {
    }

    @Around("redisCacheAspect()")
    public Object cache(ProceedingJoinPoint joinPoint) {
        // 得到類名、方法名和參數(shù)
        String clazzName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        // 根據(jù)類名邻辉、方法名和參數(shù)生成Key
        logger.info("key參數(shù): " + clazzName + "." + methodName);
        //System.out.println("key參數(shù): " + clazzName + "." + methodName);
        String key = getKey(clazzName, methodName, args);
        if (logger.isInfoEnabled()) {
            logger.info("生成key: " + key);
        }

        // 得到被代理的方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        // 得到被代理的方法上的注解
        Class modelType = method.getAnnotation(RedisCache.class).type();

        // 檢查Redis中是否有緩存
        String value = (String) srt.opsForHash().get(modelType.getName(), key);

        // 得到被代理方法的返回值類型
        Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();

        // result是方法的最終返回結(jié)果
        Object result = null;
        try {
            if (null == value) {
                if (logger.isInfoEnabled()) {
                    logger.info("緩存未命中");
                }

                // 調(diào)用數(shù)據(jù)庫查詢方法
                result = joinPoint.proceed(args);

                // 序列化查詢結(jié)果
                String json = FastJsonUtil.toJsonString(result);
                //String json = GsonUtil.toJson(result);
                System.out.println("打酉酢:"+json);

                // 序列化結(jié)果放入緩存
                srt.opsForHash().put(modelType.getName(), key, json);
            } else {

                // 緩存命中
                if (logger.isInfoEnabled()) {
                    logger.info("緩存命中, value = " + value);
                }

                result = value;
                // 反序列化 從緩存中拿到的json字符串
                result = FastJsonUtil.toObject(value, returnType);
                //result = GsonUtil.fromJson(value,returnType);
                System.out.println(result.toString());

                if (logger.isInfoEnabled()) {
                    logger.info("gson反序列化結(jié)果 = " + result);
                }
            }
        } catch (Throwable e) {
            logger.error("解析異常",e);
        }
        return result;
    }

    /**
     *      * 在方法調(diào)用前清除緩存,然后調(diào)用業(yè)務(wù)方法
     *      * @param joinPoint
     *      * @return
     *      * @throws Throwable
     *      
     */
    @Around("redisCacheEvict()")
    public Object evictCache(ProceedingJoinPoint joinPoint) throws Throwable {
        // 得到被代理的方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 得到被代理的方法上的注解
        Class modelType = method.getAnnotation(RedisEvict.class).type();
        if (logger.isInfoEnabled()) {
            logger.info("清空緩存 = " + modelType.getName());
        }
        // 清除對應(yīng)緩存
        srt.delete(modelType.getName());
        return joinPoint.proceed(joinPoint.getArgs());
    }

    /**
     * @param json
     * @param clazz
     * @param modelType
     * @return 反序列化json字符串
     * Question 遇到問題值骇,如何將復(fù)雜json字符串解析為復(fù)雜java object
     */
    private Object deserialize(String json, Class clazz, Class modelType) {
        // 序列化結(jié)果是List對象
        if (clazz.isAssignableFrom(List.class)) {
            return JsonUtil.jsonToList(json, modelType);
        }
        // 序列化結(jié)果是普通對象
        return JsonUtil.jsonToPojo(json, clazz);
    }

    private String serialize(Object result, Class clazz) {
        return JsonUtil.objectToJson(result);
    }

    /**
     *      * 根據(jù)類名莹菱、方法名和參數(shù)生成Key
     *      * @param clazzName
     *      * @param methodName
     *      * @param args
     *      * @return key格式:全類名|方法名|參數(shù)類型
     *      
     */
    private String getKey(String clazzName, String methodName, Object[] args) {
        StringBuilder key = new StringBuilder(clazzName);
        key.append(DELIMITER);
        key.append(methodName);
        key.append(DELIMITER);

        for (Object obj : args) {
            key.append(obj.getClass().getSimpleName());
            key.append(DELIMITER);
        }

        return key.toString();
    }
}

5.FastJsonUtil.java

 package com.fxmms.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;

import java.util.List;

/**
 * Created by mark on 16/11/30.
 * 采用阿里巴巴fastjson 進行對象&json字符串的序列化與反序列化
 */
public class FastJsonUtil {
    /**
     * @param object
     * @return 將java對象轉(zhuǎn)化為json字符串
     */
    public static String toJsonString(Object object) {
        return JSON.toJSONString(object,filter,SerializerFeature.DisableCircularReferenceDetect);
    }

    /**
     * 添加過濾器使數(shù)據(jù)庫中字段為NULL的字段為""
     */
    private static ValueFilter filter = new ValueFilter() {
        @Override
        public Object process(Object obj, String s, Object v) {
            if (v == null)
                return "";
            return v;
        }
    };
    /**
     * @param json
     * @param cla
     * @param <T>
     * @return 將json字符串轉(zhuǎn)化為java對象
     */
    public static <T> T toObject(String json, Class<T> cla) {
        return JSON.parseObject(json, cla);
    }

    public static <T> List<T> toList(String json, Class<T> t) {
        return JSON.parseArray(json, t);
    }

}

6.業(yè)務(wù)邏輯層設(shè)置緩存即擴充service-applicationContext.xml加入切面支持

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task.xsd">

    <aop:aspectj-autoproxy/>

    <!--設(shè)置定時任務(wù)-->
    <task:annotation-driven/>
    <context:component-scan base-package="com.fxmms.www" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
    <!--掃描日志切面-->
    <context:component-scan base-package="com.fxmms.common.log" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
    <!--掃描redis切面-->
    <context:component-scan base-package="com.fxmms.common.rediscache.redisaspect" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"></context:include-filter>
    </context:component-scan>
    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

</beans>

7.業(yè)務(wù)邏輯層應(yīng)用緩存

package com.fxmms.www.service;

import com.fxmms.common.jniutil.GetDownloadIDUtil;
import com.fxmms.common.macutil.CountBetweenMacByMacStr;
import com.fxmms.common.poiutil.ReadExcelUtil;
import com.fxmms.common.rediscache.redisannotation.RedisCache;
import com.fxmms.common.rediscache.redisannotation.RedisEvict;
import com.fxmms.common.ro.ControllerResult;
import com.fxmms.common.ro.DtoResultWithPageInfo;
import com.fxmms.www.dao.AdminDao;
import com.fxmms.www.dao.MacDao;
import com.fxmms.www.dao.TaskDao;
import com.fxmms.www.domain.Admin;
import com.fxmms.www.domain.Mac;
import com.fxmms.www.domain.Task;
import com.fxmms.www.dto.MacDto;
import com.fxmms.www.qo.MacQo;
import com.fxmms.www.thunderinterfaceutil.VisitThunderInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by mark on 16/11/7.
 *
 * @usage Mac地址操作業(yè)務(wù)邏輯層
 */
@Service
public class MacService {
    @Autowired
    MacDao macDao;
    @Autowired
    AdminDao adminDao;
    @Autowired
    TaskDao taskDao;

    /**
     * @param macStr
     * @param username
     * @return mac
     * @usage 判斷數(shù)據(jù)庫中是否已經(jīng)存儲過對應(yīng)的mac
     * 防止數(shù)據(jù)庫中存儲多個同樣的mac地址
     */
    @Transactional
    @RedisEvict(type=Mac.class)
    public Mac doJudgementBySingleMacStr(String macStr, String username) {
        Mac mac = macDao.getByUniqueKey("macAddr", macStr);
        if (mac == null) {
            //1.單個mac地址轉(zhuǎn)化為downloadId
            String downLoadId = GetDownloadIDUtil.getDownLoadId(macStr);
            Task task = new Task();//單個mac所屬task's id
            task.setDate(new Date());
            task.setFlag(0);//錄入未成功
            taskDao.save(task);
            Admin admin = adminDao.getByUniqueKey("userName", username);
            mac = new Mac();
            mac.setDownLoadId(downLoadId);
            mac.setAdmin(admin);
            mac.setMacAddr(macStr);
            mac.setDate(new Date());
            //設(shè)置mac狀態(tài)為init狀態(tài)
            mac.setStatus(0);
            mac.setTask(task);
            macDao.save(mac);
        }
        return mac;
    }

    /**
     * @param macStrList
     * @param username
     * @usage 判斷數(shù)據(jù)庫中是否已經(jīng)存儲過對應(yīng)的mac
     * 防止數(shù)據(jù)庫中存儲多個同樣的mac地址
     */
    @Transactional
    @RedisEvict(type=Mac.class)
    public void doJudgementBySeriseMacStr(List<String> macStrList, String username) {
        Task task = new Task();//單個mac所屬task's id
        task.setDate(new Date());
        task.setFlag(0);//初始化task 狀態(tài)為錄入未成功
        for (String macStr : macStrList) {
            Mac mac = macDao.getByUniqueKey("macAddr", macStr);
            if (mac == null) {
                //1.單個mac地址轉(zhuǎn)化為downloadId
                String downLoadId = GetDownloadIDUtil.getDownLoadId(macStr);
                taskDao.save(task);
                Admin admin = adminDao.getByUniqueKey("userName", username);
                mac = new Mac();
                mac.setDownLoadId(downLoadId);
                mac.setAdmin(admin);
                mac.setMacAddr(macStr);
                mac.setDate(new Date());
                //設(shè)置mac狀態(tài)為init狀態(tài)
                mac.setStatus(0);
                mac.setTask(task);
                macDao.save(mac);
            }
        }
    }

    /**
     * @param macStr
     * @param username
     * @return 1.單個mac地址轉(zhuǎn)化為downloadId, 并調(diào)用迅雷方接口
     * 2.調(diào)用接口之前先將地址存儲為數(shù)據(jù)庫中一條記錄,狀態(tài)置為0-初始化狀態(tài)
     * 3.調(diào)用完接口根據(jù)返回狀態(tài)吱瘩,將返回狀態(tài)為success的數(shù)據(jù)置為1-正在錄入
     */
    @Transactional
    @RedisEvict(type=Mac.class)
    public ControllerResult addSingleMac(String macStr, String username) {
        if (macStr == null || ("".equals(macStr))) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起道伟,MAC地址不能為空");
        }
        if (!CountBetweenMacByMacStr.matchMacAddrByregex(macStr)) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起,MAC地址格式不正確");
        }
        List<String> macStrList = new ArrayList<>();
        macStrList.add(macStr);
        Mac mac = doJudgementBySingleMacStr(macStr, username);
        //調(diào)用迅雷錄入接口使碾。
        if (VisitThunderInterface.addDownLoadId(macStrList)) {
            Admin admin = adminDao.getByUniqueKey("userName", username);
            if (mac.getStatus() != 2) {
                mac.setStatus(1);
                mac.setDate(new Date());
                mac.setAdmin(admin);
                macDao.update(mac);
            }
            return ControllerResult.valueOf(ControllerResult.SUCCESS, "迅雷錄入接口請求成功", mac);
        } else {
            Admin admin = adminDao.getByUniqueKey("userName", username);
            if (mac.getStatus() != 2) {
                mac.setStatus(3);
                mac.setDate(new Date());
                mac.setAdmin(admin);
                macDao.update(mac);
                return ControllerResult.valueOf(ControllerResult.ERROR, "對不起,請求迅雷錄入接口失敗!<a href='admin/addsinglemac'>重新錄入</a>");
            }
            return ControllerResult.valueOf(ControllerResult.ERROR, "此條mac地址已經(jīng)錄入成功");
        }
    }

    /**
     * @param startMacStr
     * @param endMacStr
     * @param username
     * @return
     * @usage 批量區(qū)間錄入業(yè)務(wù)邏輯方法
     */
    @Transactional
    @RedisEvict(type=Mac.class)
    public ControllerResult addSeriseMac(String startMacStr, String endMacStr, String username) {
        if (startMacStr == null || ("".equals(startMacStr)) || endMacStr == null || ("".equals(endMacStr))) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起蜜徽,MAC地址不能為空");
        }
        if (!CountBetweenMacByMacStr.matchMacAddrByregex(startMacStr) || !CountBetweenMacByMacStr.matchMacAddrByregex(endMacStr)) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起,MAC地址格式不正確");
        }
        List<String> macStrList = CountBetweenMacByMacStr.countBetweenMacByMacStr(startMacStr, endMacStr);
        if (macStrList.size() > 1000) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起票摇,MAC區(qū)間太長拘鞋,請拆分后錄入。<a href='admin/addserisemacs'>重新錄入</a>");
        }
        doJudgementBySeriseMacStr(macStrList, username);
        if (VisitThunderInterface.addDownLoadId(macStrList)) {
            for (String macStr : macStrList) {
                Mac mac = macDao.getByUniqueKey("macAddr", macStr);
                Admin admin = adminDao.getByUniqueKey("userName", username);
                if (mac.getStatus() != 2) {
                    mac.setStatus(1);
                    mac.setDate(new Date());
                    mac.setAdmin(admin);
                    macDao.update(mac);
                }
            }
            return ControllerResult.valueOf(ControllerResult.SUCCESS, "錄入成功");
        } else {
            for (String macStr : macStrList) {
                Mac mac = macDao.getByUniqueKey("macAddr", macStr);
                Admin admin = adminDao.getByUniqueKey("userName", username);
                if (mac.getStatus() != 2) {
                    mac.setStatus(3);
                    mac.setDate(new Date());
                    mac.setAdmin(admin);
                    macDao.update(mac);
                }
            }
            return ControllerResult.valueOf(ControllerResult.ERROR, "對不起,請求迅雷錄入接口失敗!<a href='admin/addserisemacs'>重新錄入</a>");
        }
    }

    /**
     * @param macQo
     * @return
     * @usage 獲取所有的mac錄入狀態(tài)數(shù)據(jù)業(yè)務(wù)邏輯方法
     */
    @RedisCache(type=Mac.class)
    @Transactional
    public ControllerResult getAllMacStatus(MacQo macQo) {
        DtoResultWithPageInfo<MacDto> info = macDao.queryPageListByCriteriaWithQo(macQo, MacDto.class);
        return ControllerResult.valueOf(ControllerResult.SUCCESS, "獲取mac錄入狀態(tài)成功", info);
    }

    /**
     * @param serverFile
     * @param username
     * @return
     * @usage 非連續(xù)mac地址錄入邏輯方法
     */
    @Transactional
    @RedisEvict(type=Mac.class)
    public ControllerResult addNoOrderMac(File serverFile, String username) {
        ReadExcelUtil readExcelUtil = new ReadExcelUtil();
        try {
            List<String> macStrList = readExcelUtil.readUploadMacFile(serverFile);
            if (macStrList.size() == 0 || macStrList == null) {
                return ControllerResult.valueOf(ControllerResult.ERROR, "對不起矢门,文件中MAC數(shù)據(jù)不能為空");
            }
            if (macStrList.size() > 1000) {
                return ControllerResult.valueOf(ControllerResult.ERROR, "對不起盆色,文件中數(shù)據(jù)超過1000條,請進行拆分后上傳颅和!");
            }
            for (String inFilemacStr : macStrList) {
                if (!CountBetweenMacByMacStr.matchMacAddrByregex(inFilemacStr)) {
                    return ControllerResult.valueOf(ControllerResult.ERROR, "對不起傅事,文件中有不合法的MAC地址");
                }
            }
            doJudgementBySeriseMacStr(macStrList, username);
            if (VisitThunderInterface.addDownLoadId(macStrList)) {
                for (String macStr : macStrList) {
                    Mac mac = macDao.getByUniqueKey("macAddr", macStr);
                    Admin admin = adminDao.getByUniqueKey("userName", username);
                    if (mac.getStatus() != 2) {
                        mac.setStatus(1);
                        mac.setDate(new Date());
                        mac.setAdmin(admin);
                        macDao.update(mac);
                    }
                }
                return ControllerResult.valueOf(ControllerResult.SUCCESS, "請求迅雷錄入接口成功");
            } else {
                for (String macStr : macStrList) {
                    Mac mac = macDao.getByUniqueKey("macAddr", macStr);
                    Admin admin = adminDao.getByUniqueKey("userName", username);
                    if (mac.getStatus() != 2) {
                        mac.setStatus(3);
                        mac.setAdmin(admin);
                        mac.setDate(new Date());
                        macDao.update(mac);
                    }
                }
                return ControllerResult.valueOf(ControllerResult.ERROR, "對不起,請求迅雷錄入接口失敗!<a href='admin/loadnoordermacs'>重新錄入</a>");
            }
        } catch (Exception e) {
            return ControllerResult.valueOf(ControllerResult.ERROR, "文件上傳失敗");
        }

    }
}

注意:

  • 上述程序中為非查詢方法上加上了 @RedisEvict注解,表示刪除舊的緩存峡扩。
  • 上述程序中為查詢方法上加上了 @RedisCache注解蹭越,表示為查詢業(yè)務(wù)邏輯應(yīng)用緩存,應(yīng)用邏輯為:項目中緩存數(shù)據(jù)的Struct為Hash教届,每張表對應(yīng)的實體類使用一個名為Key的Hash結(jié)構(gòu)來存儲數(shù)據(jù)响鹃,當(dāng)訪問的key 存在時,直接從緩存中取出數(shù)據(jù)案训,不存在時第一步先從數(shù)據(jù)庫中查詢數(shù)據(jù)买置,再生成key,并生成對應(yīng)的filed與value强霎。

程序運行結(jié)果:

2016-12-03 20:16:05,212 [INFO]-[com.fxmms.common.rediscache.redisaspect.RedisCacheAspect.cache(RedisCacheAspect.java:67)] key參數(shù): com.fxmms.www.service.MacService.getAllMacStatus
2016-12-03 20:16:05,219 [INFO]-[com.fxmms.common.rediscache.redisaspect.RedisCacheAspect.cache(RedisCacheAspect.java:71)] 生成key: com.fxmms.www.service.MacService|getAllMacStatus|MacQo|
2016-12-03 20:16:05,357 [INFO]-[com.fxmms.common.rediscache.redisaspect.RedisCacheAspect.cache(RedisCacheAspect.java:108)] 緩存命中, value = {"msg":"獲取mac錄入狀態(tài)成功","result":"success","rows":{"emptyResult":false,"pageInfo":{"firstPage":true,"firstResultNum":0,"lastPage":false,"lastResultNum":10,"pageNo":1,"pageSize":10,"totalPage":49,"totalQuantity":488},"results":[{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479913221000,"dateStr":"2016-11-23 23:00:21","deviceId":"730CBAEA-6954-000A-2D77-BAF544E6F192","downLoadId":"11123E566745FB30FE5C9AC094A1BAA0","id":488,"macAddr":"11:12:3e:56:67:45","status":2,"statusStr":"<span class=\"label label-success\">錄入成功</span>","task":{"date":1479913220000,"flag":1,"id":29}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479448899000,"dateStr":"2016-11-18 14:01:39","deviceId":"","downLoadId":"34BDF9C0B2C1EC6B5CA3B81DCB05241D","id":487,"macAddr":"34:BD:F9:C0:B2:c1","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479448898000,"flag":0,"id":28}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479448476000,"dateStr":"2016-11-18 13:54:36","deviceId":"","downLoadId":"11123E586745088C6CAF8E6C2EBDB7A5","id":486,"macAddr":"11:12:3e:58:67:45","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479448476000,"flag":0,"id":27}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479447598000,"dateStr":"2016-11-18 13:39:58","deviceId":"","downLoadId":"34BDFAC0B2F01A731572C0BCEC4D26F0","id":485,"macAddr":"34:BD:FA:C0:B2:F0","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479447598000,"flag":0,"id":26}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479447575000,"dateStr":"2016-11-18 13:39:35","deviceId":"","downLoadId":"3EBDF9C0B2F02D7F2A6CAC4F2B5121E8","id":484,"macAddr":"3e:BD:F9:C0:B2:F0","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479447575000,"flag":0,"id":25}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479446783000,"dateStr":"2016-11-18 13:26:23","deviceId":"","downLoadId":"11128E566749F8776504253D15D8B001","id":483,"macAddr":"11:12:8e:56:67:49","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479446783000,"flag":0,"id":24}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479446754000,"dateStr":"2016-11-18 13:25:54","deviceId":"","downLoadId":"11128E566745B130B2E6C6AA8E52EB4A","id":482,"macAddr":"11:12:8e:56:67:45","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479446753000,"flag":0,"id":23}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479446736000,"dateStr":"2016-11-18 13:25:36","deviceId":"","downLoadId":"341DF9C0B2F11E391DDA8EDAB78B4162","id":481,"macAddr":"34:1D:F9:C0:B2:F1","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479446736000,"flag":0,"id":22}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479437904000,"dateStr":"2016-11-18 10:58:24","deviceId":"","downLoadId":"11446633889613659EE26ABE4FBE28CD","id":480,"macAddr":"11:44:66:33:88:96","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479437904000,"flag":0,"id":21}},{"admin":{"enable":1,"id":1,"isDelete":0,"password":"11","role":"admin","userName":"ls"},"date":1479437899000,"dateStr":"2016-11-18 10:58:19","deviceId":"","downLoadId":"1144663388947CCC987231F802C72F83","id":479,"macAddr":"11:44:66:33:88:94","status":3,"statusStr":"<span class=\"label label-danger\">錄入失敗</span>","task":{"date":1479437899000,"flag":0,"id":20}}]},"total":0}

完忿项。

福利彩蛋

職位:騰訊OMG 廣告后臺高級開發(fā)工程師;
Base:深圳;
場景:海量數(shù)據(jù),To B,To C轩触,場景極具挑戰(zhàn)性寞酿。
基礎(chǔ)要求:
熟悉常用數(shù)據(jù)結(jié)構(gòu)與算法;
熟悉常用網(wǎng)絡(luò)協(xié)議,熟悉網(wǎng)絡(luò)編程;
熟悉操作系統(tǒng)脱柱,有線上排查問題經(jīng)驗;
熟悉MySQL,oracle;
熟悉JAVA伐弹,GoLang,c++其中一種語言均可;
可內(nèi)推榨为,歡迎各位優(yōu)秀開發(fā)道友私信[微笑]
期待關(guān)注我的開發(fā)小哥哥惨好,小姐姐們私信我,機會很好随闺,平臺對標(biāo)抖音日川,廣告生態(tài)平臺,類似Facebook 廣告平臺矩乐,希望你們用簡歷砸我~
聯(lián)系方式 微信 13609184526

博客搬家:大坤的個人博客
歡迎評論哦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逗鸣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绰精,更是在濱河造成了極大的恐慌撒璧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笨使,死亡現(xiàn)場離奇詭異卿樱,居然都是意外死亡,警方通過查閱死者的電腦和手機硫椰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門繁调,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靶草,你說我怎么就攤上這事蹄胰。” “怎么了奕翔?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵裕寨,是天一觀的道長。 經(jīng)常有香客問我派继,道長宾袜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任驾窟,我火速辦了婚禮庆猫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绅络。我一直安慰自己月培,他們只是感情好嘁字,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杉畜,像睡著了一般拳锚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寻行,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音匾荆,去河邊找鬼拌蜘。 笑死,一個胖子當(dāng)著我的面吹牛牙丽,可吹牛的內(nèi)容都是我干的简卧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼烤芦,長吁一口氣:“原來是場噩夢啊……” “哼举娩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起构罗,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铜涉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遂唧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芙代,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年盖彭,在試婚紗的時候發(fā)現(xiàn)自己被綠了纹烹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡召边,死狀恐怖铺呵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隧熙,我是刑警寧澤片挂,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站贞盯,受9級特大地震影響宴卖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邻悬,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一症昏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧父丰,春花似錦肝谭、人聲如沸掘宪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魏滚。三九已至,卻和暖如春坟漱,著一層夾襖步出監(jiān)牢的瞬間鼠次,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工芋齿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腥寇,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓觅捆,卻偏偏與公主長得像赦役,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子栅炒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理掂摔,服務(wù)發(fā)現(xiàn),斷路器赢赊,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評論 6 342
  • 從三月份找實習(xí)到現(xiàn)在乙漓,面了一些公司,掛了不少释移,但最終還是拿到小米簇秒、百度、阿里秀鞭、京東趋观、新浪、CVTE锋边、樂視家的研發(fā)崗...
    時芥藍閱讀 42,184評論 11 349
  • 其實今天的“簡書”算不上什么皱坛,倒是因為生活平淡瑣碎,引發(fā)而起攝影雜記豆巨。熟悉我的人剩辟,向來知道我所寫的記的不過是我直言...
    AI生活閱讀 416評論 3 3
  • 真的好累
    就是一個別扭的人閱讀 173評論 0 0