java設(shè)計模式(實戰(zhàn))-責(zé)任鏈模式
一:模式說明
模式定義:使多個對象都有機會處理請求,從而避免了請求的發(fā)送者和接受者之間的耦合關(guān)系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有對象處理它為止。
責(zé)任鏈模式的重點是在“鏈”上,由一條鏈去處理相似的請求在鏈中決定誰來處理這個請求,并返回相應(yīng)的結(jié)果(取自《設(shè)計模式之禪》)。
翻譯:Client對象調(diào)用一個處理者(類)的方法,可能有多個處理者(實現(xiàn)類),但是該對象只需要調(diào)用第一個處理者(類)即可,該模式會自動分配誰來處理這個請求;這多個處理者繼承同一個父類(即在一條鏈上)。
通用類圖如下:
Client發(fā)送請求到Handler,Handler自動分配請求到子類的實現(xiàn)類ConcreteHandler中。
二:項目實戰(zhàn)
在文章 >手寫redis@Cacheable注解 支持過期時間設(shè)置< 的基礎(chǔ)之上做修改,原版為redis緩存注解實現(xiàn),
原版實現(xiàn)功能:
- 將數(shù)據(jù)存放到redis中
- 設(shè)置過期時間
原業(yè)務(wù)邏輯查詢?nèi)藛T列表listleader()接口,數(shù)據(jù)存放redis中,減少數(shù)據(jù)庫負(fù)載。
由于業(yè)務(wù)發(fā)展,需要進一步優(yōu)化查詢接口;目前每個人都會操作redis中存放的人員列表,導(dǎo)致該列表會實時發(fā)生變動(比如
每個人員對應(yīng)的分?jǐn)?shù)),每個人都有自己的緩存人員列表而不是統(tǒng)一的人員列表;原列表已經(jīng)無法滿足現(xiàn)需求,每個人第一次登
錄都會查詢數(shù)據(jù)庫,將自己的列表存放在redis中。
解決方法:設(shè)置兩級緩存,第一級為該用戶(uuid)唯一緩存,key值設(shè)置為參數(shù)1+uuid+參數(shù)2;第二級為第一次登錄查詢返
回redis中的原始leader列表,key值設(shè)置為參數(shù)1+參數(shù)2。如果當(dāng)前用戶leader列表(一級緩存)為空,則查詢原始leader列表
(二級緩存),在操作分?jǐn)?shù)的時候修改二級緩存(初始人員列表)來產(chǎn)生一級緩存,存放進redis,減少了數(shù)據(jù)庫的直接訪問。
項目中責(zé)任鏈相關(guān)設(shè)計類圖如下:
說明:抽象類CacheHandler 一是定義了處理請求方法handleMessage;二是定義一個鏈的編排方法setNext,設(shè)置下一個處理者;三是定義了具體的請求者必須實現(xiàn)的兩個方法:定義自己能夠處理的級別getHandlerLevel和具體的處理任務(wù)response;
FirstCacheHadler為一級緩存處理者,SecondCacheHadler為二級緩存處理者。緩存處理的方式通過CacheableAspect類調(diào)用。
三:源代碼
CacheableAspect:client調(diào)用
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緩存處理 不適用與內(nèi)部方法調(diào)用(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 { // 獲得當(dāng)前訪問的class Class<?> className = joinPoint.getTarget().getClass(); // 獲得訪問的方法名 String methodName = joinPoint.getSignature().getName(); // 得到方法的參數(shù)的類型 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緩存注解參數(shù)異常", e); } //責(zé)任鏈模式 CacheHandler firstCacheHadler = new FirstCacheHadler(); CacheHandler secondCacheHadler = new SecondCacheHadler(); //設(shè)置下級處理者 firstCacheHadler.setNext(secondCacheHadler); //獲取處理級別 int cacheLevel = getCacheLevel(key, nextKey); RedisResult result = new RedisResult(redisUtil, key, nextKey, joinPoint, cacheLevel, expireTime); //客戶端調(diào)用 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中的參數(shù)名稱 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 緩存處理接口 * 責(zé)任鏈模式 */ public abstract class CacheHandler { // 定義處理級別 protected final static int FirstCache_LEVEL_REQUEST = 1; protected final static int SecondCache_LEVEL_REQUEST = 2; // 能處理的級別 private int level = 0; // 責(zé)任傳遞,下一個責(zé)任人是誰 private CacheHandler nextHandler; // 每個類自己能處理那些請求 public CacheHandler(int level) { this.level = level; } // 處理請求 public final Object HandleMessage(RedisResult redisResult) throws Throwable { //如果women類型為當(dāng)前處理的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; } // 有請示的回應(yīng) 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:該業(yè)務(wù)場景對象,用于傳遞參數(shù)
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; }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java concurrency集合之CopyOnWriteArraySet_動力節(jié)點Java學(xué)院整理
CopyOnWriteArraySet基于CopyOnWriteArrayList實現(xiàn),其唯一的不同是在add時調(diào)用的是CopyOnWriteArrayList的addIfAbsent(若沒有則增加)方法2017-06-06Java基于JavaMail實現(xiàn)向QQ郵箱發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了Java基于JavaMail實現(xiàn)向QQ郵箱發(fā)送郵件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-01-01HashMap紅黑樹入門(實現(xiàn)一個簡單的紅黑樹)
紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,是在計算機科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實現(xiàn)關(guān)聯(lián)數(shù)組。 紅黑樹發(fā)明時被稱為平衡二叉B樹,后來修改為如今的“紅黑樹”2021-06-06通過Feign進行調(diào)用@FeignClient?找不到的解決方案
這篇文章主要介紹了通過Feign進行調(diào)用@FeignClient?找不到的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03RocketMQMessageListener注解對rocketmq消息的消費實現(xiàn)機制
這篇文章主要為大家介紹了RocketMQMessageListener注解對rocketmq消息的消費實現(xiàn)機制源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10