設計模式(實戰)-責任鏈模式
一:模式說明
模式定義:使多個物件都有機會處理請求,從而避免了請求的傳送者和接受者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有物件處理它為止。
責任鏈模式的重點是在“鏈”上,由一條鏈去處理相似的請求在鏈中決定誰來處理這個請求,並返回相應的結果(取自《設計模式之禪》)。
翻譯:Client物件呼叫一個處理者(類)的方法,可能有多個處理者(實現類),但是該物件只需要呼叫第一個處理者(類)即可,該模式會自動分配誰來處理這個請求;這多個處理者繼承同一個父類(即在一條鏈上)。
通用類圖如下:
Client傳送請求到Handler,Handler自動分配請求到子類的實現類ConcreteHandler中。
二:專案實戰
在文章 >手寫[email protected]註解 支援過期時間設定< 的基礎之上做修改,原版為redis快取註解實現,
原版實現功能:
- 將資料存放到redis中
- 設定過期時間
原業務邏輯查詢人員列表listleader()介面,資料存放redis中,減少資料庫負載。
由於業務發展,需要進一步優化查詢介面;目前每個人都會操作redis中存放的人員列表,導致該列表會實時發生變動(比如
每個人員對應的分數),每個人都有自己的快取人員列表而不是統一的人員列表;原列表已經無法滿足現需求,每個人第一次登
錄都會查詢資料庫,將自己的列表存放在redis中。
解決方法:設定兩級快取,第一級為該使用者(uuid)唯一快取,key值設定為引數1+uuid+引數2;第二級為第一次登入查詢返
回redis中的原始leader列表,key值設定為引數1+引數2。如果當前使用者leader列表(一級快取)為空,則查詢原始leader列表
(二級快取),在操作分數的時候修改二級快取(初始人員列表)來產生一級快取,存放進redis,減少了資料庫的直接訪問。
專案中責任鏈相關設計類圖如下:
說明:抽象類CacheHandler 一是定義了處理請求方法handleMessage;二是定義一個鏈的編排方法setNext,設定下一個處理者;三是定義了具體的請求者必須實現的兩個方法:定義自己能夠處理的級別getHandlerLevel和具體的處理任務response;
FirstCacheHadler為一級快取處理者,SecondCacheHadler為二級快取處理者。快取處理的方式通過CacheableAspect類呼叫。
三:原始碼
CacheableAspect:client呼叫
package com.huajie.aspect;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
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.stereotype.Component;
import com.huajie.annotation.ExtCacheable;
import com.huajie.common.cache.CacheHandler;
import com.huajie.common.cache.FirstCacheHadler;
import com.huajie.common.cache.RedisResult;
import com.huajie.common.cache.SecondCacheHadler;
import com.huajie.utils.RedisUtil;
import com.huajie.utils.StringUtil;
/**
* redis快取處理 不適用與內部方法呼叫(this.)或者private
*/
@Component
@Aspect
public class CacheableAspect {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(com.huajie.annotation.ExtCacheable)")
public void annotationPointcut() {
}
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 獲得當前訪問的class
Class<?> className = joinPoint.getTarget().getClass();
// 獲得訪問的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的引數的型別
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
String nextKey = "";
int expireTime = 1800;
try {
// 得到訪問的方法物件
Method method = className.getMethod(methodName, argClass);
method.setAccessible(true);
// 判斷是否存在@ExtCacheable註解
if (method.isAnnotationPresent(ExtCacheable.class)) {
ExtCacheable annotation = method.getAnnotation(ExtCacheable.class);
key = getRedisKey(args, annotation.key());
nextKey = getRedisKey(args,annotation.nextKey());
expireTime = getExpireTime(annotation);
}
} catch (Exception e) {
throw new RuntimeException("redis快取註解引數異常", e);
}
//責任鏈模式
CacheHandler firstCacheHadler = new FirstCacheHadler();
CacheHandler secondCacheHadler = new SecondCacheHadler();
//設定下級處理者
firstCacheHadler.setNext(secondCacheHadler);
//獲取處理級別
int cacheLevel = getCacheLevel(key, nextKey);
RedisResult result = new RedisResult(redisUtil, key, nextKey, joinPoint, cacheLevel, expireTime);
//客戶端呼叫
return firstCacheHadler.HandleMessage(result);
}
private int getCacheLevel(String key, String nextKey) {
if (StringUtil.isNotEmpty(key) && StringUtil.isNotEmpty(nextKey)) {
return 2;
} else {
return 1;
}
}
private int getExpireTime(ExtCacheable annotation) {
return annotation.expireTime();
}
private String getRedisKey(Object[] args, String primalKey) {
// 獲取#p0...集合
List<String> keyList = getKeyParsList(primalKey);
for (String keyName : keyList) {
int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
Object parValue = args[keyIndex];
primalKey = primalKey.replace(keyName, String.valueOf(parValue));
}
return primalKey.replace("+", "").replace("'", "");
}
// 獲取key中#p0中的引數名稱
private static List<String> getKeyParsList(String key) {
List<String> ListPar = new ArrayList<String>();
if (key.indexOf("#") >= 0) {
int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
int indexNext = 0;
String parName = "";
int indexPre = key.indexOf("#");
if (plusIndex > 0) {
indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
parName = key.substring(indexPre, indexNext);
} else {
parName = key.substring(indexPre);
}
ListPar.add(parName.trim());
key = key.substring(indexNext + 1);
if (key.indexOf("#") >= 0) {
ListPar.addAll(getKeyParsList(key));
}
}
return ListPar;
}
}
CacheHandler:
package com.huajie.common.cache;
/**
* @author xiewenfeng 快取處理介面
* 責任鏈模式
*/
public abstract class CacheHandler {
// 定義處理級別
protected final static int FirstCache_LEVEL_REQUEST = 1;
protected final static int SecondCache_LEVEL_REQUEST = 2;
// 能處理的級別
private int level = 0;
// 責任傳遞,下一個責任人是誰
private CacheHandler nextHandler;
// 每個類自己能處理那些請求
public CacheHandler(int level) {
this.level = level;
}
// 處理請求
public final Object HandleMessage(RedisResult redisResult) throws Throwable {
//如果women型別為當前處理的level
if(redisResult.getCacheLevel()==this.level){
return this.response(redisResult);
}else{
if(null!=this.nextHandler){
return this.nextHandler.HandleMessage(redisResult);
}else{
//沒有下級不處理
return null;
}
}
}
public void setNext(CacheHandler handler) {
this.nextHandler = handler;
}
// 有請示的迴應
protected abstract Object response(RedisResult redisResult) throws Throwable;
}
FirstCacheHadler:一級快取處理者
package com.huajie.common.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import com.huajie.utils.RedisUtil;
public class FirstCacheHadler extends CacheHandler{
public FirstCacheHadler() {
super(CacheHandler.FirstCache_LEVEL_REQUEST);
}
@Override
protected Object response(RedisResult redisResult) throws Throwable {
String key = redisResult.getKey();
RedisUtil redisUtil = redisResult.getRedisUtil();
boolean hasKey = redisUtil.hasKey(key);
ProceedingJoinPoint joinPoint = redisResult.getJoinPoint();
int expireTime = redisResult.getExpireTime();
if (hasKey) {
return redisUtil.get(key);
} else {
Object res = joinPoint.proceed();
redisUtil.set(key, res);
redisUtil.expire(key, expireTime);
return res;
}
}
}
SecondCacheHadler:二級快取處理者
package com.huajie.common.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import com.huajie.utils.RedisUtil;
public class SecondCacheHadler extends CacheHandler {
public SecondCacheHadler() {
super(CacheHandler.SecondCache_LEVEL_REQUEST);
}
@Override
protected Object response(RedisResult redisResult) throws Throwable {
String nextKey = redisResult.getNextKey();
String key = redisResult.getKey();
RedisUtil redisUtil = redisResult.getRedisUtil();
ProceedingJoinPoint joinPoint = redisResult.getJoinPoint();
int expireTime = redisResult.getExpireTime();
boolean hasKey = redisUtil.hasKey(key);
if (hasKey) {
return redisUtil.get(key);
} else {
boolean hasNextKey = redisUtil.hasKey(nextKey);
if (hasNextKey) {
return redisUtil.get(nextKey);
} else {
Object res = joinPoint.proceed();
redisUtil.set(nextKey, res);
redisUtil.expire(nextKey, expireTime);
return res;
}
}
}
}
RedisResult:該業務場景物件,用於傳遞引數
package com.huajie.common.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import com.huajie.utils.RedisUtil;
import lombok.Data;
@Data
public class RedisResult implements IRedisResult {
private int cacheLevel;
private Object result;
private RedisUtil redisUtil;
private String key;
private String nextKey;
private int expireTime;
private ProceedingJoinPoint joinPoint;
@Override
public int getCacheLevel() {
return cacheLevel;
}
@Override
public Object getResult() {
return result;
}
public RedisResult(RedisUtil redisUtil, String key, String nextKey, ProceedingJoinPoint joinPoint, int cacheLevel,int expireTime) {
this.redisUtil = redisUtil;
this.key = key;
this.joinPoint = joinPoint;
this.cacheLevel = cacheLevel;
this.nextKey = nextKey;
this.expireTime = expireTime;
}
}
使用方法如下:
@Override
@ExtCacheable(key = "middle+#p0+#p1+#p2", nextKey = "middle+#p0+#p2")
public List<MiddleManage> listMiddleManageInfo(String leadergroupId, String uuid, String yearDetailId) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("leadergroupId", leadergroupId);
map.put("uuid", uuid);
map.put("yearDetailId", yearDetailId);
List<MiddleManage> middleManageDetailList = middleManageMapper.listMiddleManageInfo(map);
return middleManageDetailList;
}