應(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
博客搬家:大坤的個人博客
歡迎評論哦~