java設(shè)計(jì)模式(實(shí)戰(zhàn))-責(zé)任鏈模式
一:模式說(shuō)明
模式定義:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免了請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止。
責(zé)任鏈模式的重點(diǎn)是在“鏈”上,由一條鏈去處理相似的請(qǐng)求在鏈中決定誰(shuí)來(lái)處理這個(gè)請(qǐng)求,并返回相應(yīng)的結(jié)果(取自《設(shè)計(jì)模式之禪》)。
翻譯:Client對(duì)象調(diào)用一個(gè)處理者(類(lèi))的方法,可能有多個(gè)處理者(實(shí)現(xiàn)類(lèi)),但是該對(duì)象只需要調(diào)用第一個(gè)處理者(類(lèi))即可,該模式會(huì)自動(dòng)分配誰(shuí)來(lái)處理這個(gè)請(qǐng)求;這多個(gè)處理者繼承同一個(gè)父類(lèi)(即在一條鏈上)。
通用類(lèi)圖如下:

Client發(fā)送請(qǐng)求到Handler,Handler自動(dòng)分配請(qǐng)求到子類(lèi)的實(shí)現(xiàn)類(lèi)ConcreteHandler中。
二:項(xiàng)目實(shí)戰(zhàn)
在文章 >手寫(xiě)redis@Cacheable注解 支持過(guò)期時(shí)間設(shè)置< 的基礎(chǔ)之上做修改,原版為redis緩存注解實(shí)現(xiàn),
原版實(shí)現(xiàn)功能:
- 將數(shù)據(jù)存放到redis中
- 設(shè)置過(guò)期時(shí)間
原業(yè)務(wù)邏輯查詢?nèi)藛T列表listleader()接口,數(shù)據(jù)存放redis中,減少數(shù)據(jù)庫(kù)負(fù)載。
由于業(yè)務(wù)發(fā)展,需要進(jìn)一步優(yōu)化查詢接口;目前每個(gè)人都會(huì)操作redis中存放的人員列表,導(dǎo)致該列表會(huì)實(shí)時(shí)發(fā)生變動(dòng)(比如
每個(gè)人員對(duì)應(yīng)的分?jǐn)?shù)),每個(gè)人都有自己的緩存人員列表而不是統(tǒng)一的人員列表;原列表已經(jīng)無(wú)法滿足現(xiàn)需求,每個(gè)人第一次登
錄都會(huì)查詢數(shù)據(jù)庫(kù),將自己的列表存放在redis中。
解決方法:設(shè)置兩級(jí)緩存,第一級(jí)為該用戶(uuid)唯一緩存,key值設(shè)置為參數(shù)1+uuid+參數(shù)2;第二級(jí)為第一次登錄查詢返
回redis中的原始leader列表,key值設(shè)置為參數(shù)1+參數(shù)2。如果當(dāng)前用戶leader列表(一級(jí)緩存)為空,則查詢?cè)糽eader列表
(二級(jí)緩存),在操作分?jǐn)?shù)的時(shí)候修改二級(jí)緩存(初始人員列表)來(lái)產(chǎn)生一級(jí)緩存,存放進(jìn)redis,減少了數(shù)據(jù)庫(kù)的直接訪問(wèn)。
項(xiàng)目中責(zé)任鏈相關(guān)設(shè)計(jì)類(lèi)圖如下:

說(shuō)明:抽象類(lèi)CacheHandler 一是定義了處理請(qǐng)求方法handleMessage;二是定義一個(gè)鏈的編排方法setNext,設(shè)置下一個(gè)處理者;三是定義了具體的請(qǐng)求者必須實(shí)現(xiàn)的兩個(gè)方法:定義自己能夠處理的級(jí)別getHandlerLevel和具體的處理任務(wù)response;
FirstCacheHadler為一級(jí)緩存處理者,SecondCacheHadler為二級(jí)緩存處理者。緩存處理的方式通過(guò)CacheableAspect類(lèi)調(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)前訪問(wèn)的class
Class<?> className = joinPoint.getTarget().getClass();
// 獲得訪問(wèn)的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的參數(shù)的類(lèi)型
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
String nextKey = "";
int expireTime = 1800;
try {
// 得到訪問(wèn)的方法對(duì)象
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è)置下級(jí)處理者
firstCacheHadler.setNext(secondCacheHadler);
//獲取處理級(jí)別
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 {
// 定義處理級(jí)別
protected final static int FirstCache_LEVEL_REQUEST = 1;
protected final static int SecondCache_LEVEL_REQUEST = 2;
// 能處理的級(jí)別
private int level = 0;
// 責(zé)任傳遞,下一個(gè)責(zé)任人是誰(shuí)
private CacheHandler nextHandler;
// 每個(gè)類(lèi)自己能處理那些請(qǐng)求
public CacheHandler(int level) {
this.level = level;
}
// 處理請(qǐng)求
public final Object HandleMessage(RedisResult redisResult) throws Throwable {
//如果women類(lèi)型為當(dāng)前處理的level
if(redisResult.getCacheLevel()==this.level){
return this.response(redisResult);
}else{
if(null!=this.nextHandler){
return this.nextHandler.HandleMessage(redisResult);
}else{
//沒(méi)有下級(jí)不處理
return null;
}
}
}
public void setNext(CacheHandler handler) {
this.nextHandler = handler;
}
// 有請(qǐng)示的回應(yīng)
protected abstract Object response(RedisResult redisResult) throws Throwable;
}FirstCacheHadler:一級(jí)緩存處理者
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:二級(jí)緩存處理者
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ù)場(chǎng)景對(duì)象,用于傳遞參數(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;
}以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Java設(shè)計(jì)模式之責(zé)任鏈模式(Chain of Responsibility模式)介紹
- Java 中責(zé)任鏈模式實(shí)現(xiàn)的三種方式
- 詳解Java實(shí)現(xiàn)設(shè)計(jì)模式之責(zé)任鏈模式
- Java責(zé)任鏈模式詳解
- JAVA設(shè)計(jì)模式之責(zé)任鏈模式詳解
- Java設(shè)計(jì)模式之責(zé)任鏈模式簡(jiǎn)介
- Java責(zé)任鏈模式定義與用法分析
- Java責(zé)任鏈設(shè)計(jì)模式
- Java實(shí)現(xiàn)設(shè)計(jì)模式之責(zé)任鏈模式
- Java責(zé)任鏈模式的實(shí)現(xiàn)方法詳解
相關(guān)文章
SpringBoot3整合Swagger3時(shí)出現(xiàn)Type javax.servlet.http.H的ttpSe
這篇文章主要介紹了SpringBoot3整合Swagger3時(shí)出現(xiàn)Type javax.servlet.http.H的ttpServletRequest not present錯(cuò)誤解決方法,文中有詳細(xì)的解決方法,需要的朋友可以參考下2025-01-01
解決@PathVariable出現(xiàn)點(diǎn)號(hào).時(shí)導(dǎo)致路徑參數(shù)截?cái)喃@取不全的問(wèn)題
這篇文章主要介紹了解決@PathVariable出現(xiàn)點(diǎn)號(hào).時(shí)導(dǎo)致路徑參數(shù)截?cái)喃@取不全的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
java多線程之wait(),notify(),notifyAll()的詳解分析
本篇文章是對(duì)java多線程 wait(),notify(),notifyAll()進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
SpringMVC配置攔截器實(shí)現(xiàn)登錄控制的方法
這篇文章主要介紹了SpringMVC配置攔截器實(shí)現(xiàn)登錄控制的方法,SpringMVC讀取Cookie判斷用戶是否登錄,對(duì)每一個(gè)action都要進(jìn)行判斷,有興趣的可以了解一下。2017-03-03
高級(jí)數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進(jìn)行字符串去重的方法實(shí)例
今天小編就為大家分享一篇關(guān)于高級(jí)數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進(jìn)行字符串去重的方法實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02
關(guān)于后端如何解決跨域的問(wèn)題說(shuō)明
這篇文章主要介紹了關(guān)于后端如何解決跨域的問(wèn)題說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Java多線程案例實(shí)戰(zhàn)之定時(shí)器的實(shí)現(xiàn)
在Java中可以使用多線程和定時(shí)器來(lái)實(shí)現(xiàn)定時(shí)任務(wù),下面這篇文章主要給大家介紹了關(guān)于Java多線程案例之定時(shí)器實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
SpringCloud開(kāi)啟session共享并存儲(chǔ)到Redis的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloud開(kāi)啟session共享并存儲(chǔ)到Redis的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

