成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊一聊責任鏈模式

開發 前端
責任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個節點看作是一個對象,每個節點處理的請求均不同,且內部自動維護一個下一節點對象。當一個請求從鏈式的首端發出時,會沿著鏈的路徑依次傳遞給每一個節點對象,直至有對象處理這個請求為止,屬于行為型模式。

一、概述

責任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個節點看作是一個對象,每個節點處理的請求均不同,且內部自動維護一個下一節點對象。當一個請求從鏈式的首端發出時,會沿著鏈的路徑依次傳遞給每一個節點對象,直至有對象處理這個請求為止,屬于行為型模式。下面放一張足球比賽的圖,通過層層傳遞,最終射門。通過這張圖,可以更好的理解責任鏈模式。

圖片

二、入門案例

2.1 類圖

圖片

2.2 基礎類介紹

抽象接口RequestHandler

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 13:41
* @description
*/
public interface RequestHandler {

void doHandler(String req);
}

抽象類BaseRequestHandler

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 13:45
* @description
*/
public abstract class BaseRequestHandler implements RequestHandler {

protected RequestHandler next;

public void next(RequestHandler next){
this.next = next;
}
}

具體處理類AHandler

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 14:00
* @description
*/
public class AHandler extends BaseRequestHandler {

@Override
public void doHandler(String req){
// 處理自己的業務邏輯
System.out.println("A中處理自己的邏輯");
// 傳遞給下個類(若鏈路中還有下個處理類)
if (next != null) {
next.doHandler(req);
}
}
}

當然還有具體的處理類B、C等等,這里不展開贅述。

 使用類Client

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 14:06
* @description
*/
public class Client {
public static void main(String[] args){
BaseRequestHandler a = new AHandler();
BaseRequestHandler b = new BHandler();
BaseRequestHandler c = new CHandler();
a.next(b);
b.next(c);
a.doHandler("鏈路待處理的數據");
}
}

2.3 處理流程圖

圖片

三、應用場景

3.1 場景舉例

場景一

前兩年,在一家金融公司待過一段時間,其中就有一個業務場景:一筆訂單進來,會先在后臺通過初審人員進行審批,初審不通過,訂單流程結束。初審通過以后,會轉給終審人員進行審批,不通過,流程結束;通過,流轉到下個業務場景。對于這塊業務代碼,之前一代目是一個叫知了的同事,他擼起袖子就是干,一套if-else干到底。后來,技術老大CodeReview,點名要求改掉這塊。于是乎,想到用用設計模式吧,然后就噼里啪啦一頓改。(當然,比較復雜的情況,還是可以用工作流來處理這個場景,當時礙于時間成本,也就放棄了)。

場景二

上家公司對接甲方爸爸的時候,對方會調用我們接口,將數據同步過來。同樣,我們需要將處理好的數據,傳給他們。由于雙方傳輸數據都是加密傳輸,所以在接受他們數據之前,需要對數據進行解密,驗簽,參數校驗等操作。同樣,我們給他們傳數據也需要進行加簽,加密操作。

具體案例

話不多說,對于場景二,我來放一些偽代碼,跟大家一起探討下。1、一切從注解開始,我這里自定義了一個注解@Duty?,這個注解有spring的@Component注解,也就是標記了這個自定義注解的類,都是交給spring的bean容器去管理。注解中,有兩個屬性:1.type,定義相同的type類型的bean,會被放到一個責任鏈集合中。2.order,同一個責任鏈集合中,bean的排序,數值越小,會放到鏈路最先的位置,優先處理。

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 16:11
* @description
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Service
public @interface Duty {
/**
* 標記具體業務場景
* @return
*/
String type() default "";

/**
* 排序:數值越小,排序越前
* @return
*/
int order() default 0;
}

2、定義一個頂層的抽象接口IHandler,傳入2個泛型參數,供后續自定義。

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 15:31
* @description 責任鏈頂層抽象類
*/
public interface IHandler<T, R> {
/**
* 抽象處理類
* @param t
* @return
*/
R handle(T t);
}

3、定義一個責任鏈bean的管理類HandleChainManager,用來存放不同業務下的責任鏈路集合。在該類中,有一個Map和兩個方法。

handleMap:這個map會存放責任鏈路中,具體的執行類,key是注解@Duty?中定義的type值,value是標記了@Duty注解的bean集合,也就是具體的執行類集合。

setHandleMap:傳入具體執行bean的集合,存放在map中。

executeHandle:從map中找到具體的執行bean集合,并依次執行。

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 16:00
* @description 責任鏈管理類
*/
public class HandleChainManager {
/**
* 存放責任鏈路上的具體處理類
* k-具體業務場景名稱
* v-具體業務場景下的責任鏈路集合
*/
private Map<String, List<IHandler>> handleMap;

/**
* 存放系統中責任鏈具體處理類
* @param handlerList
*/
public void setHandleMap(List<IHandler> handlerList){
handleMap = handlerList
.stream()
.sorted(Comparator.comparingInt(h -> AnnotationUtils.findAnnotation(h.getClass(), Duty.class).order()))
.collect(Collectors.groupingBy(handler -> AnnotationUtils.findAnnotation(handler.getClass(), Duty.class).type()));
}

/**
* 執行具體業務場景中的責任鏈集合
* @param type 對應@Duty注解中的type,可以定義為具體業務場景
* @param t 被執行的參數
*/
public <T, R> R executeHandle(String type, T t){
List<IHandler> handlers = handleMap.get(type);
R r = null;
if (CollectionUtil.isNotEmpty(handlers)) {
for (IHandler<T, R> handler : handlers) {
r = handler.handle(t);
}
}
return r;
}
}

4、定義一個配置類PatternConfiguration?,用于裝配上面的責任鏈管理器HandleChainManager。

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 15:35
* @description 設計模式配置類
*/
@Configuration
public class PatternConfiguration {

@Bean
public HandleChainManager handlerChainExecute(List<IHandler> handlers){
HandleChainManager handleChainManager = new HandleChainManager();
handleChainManager.setHandleMap(handlers);
return handleChainManager;
}

}

5、具體的處理類:SignChainHandler、EncryptionChainHandler、RequestChainHandler?,這里我以SignChainHandler?為例。在具體處理類上標記自定義注解@Duty?,該類會被注入到bean容器中,實現IHandler接口,只需關心自己的handle方法,處理具體的業務邏輯。

/**
* @author 往事如風
* @version 1.0
* @date 2022/10/25 15:31
* @description 加簽類
*/
@Duty(type = BusinessConstants.REQUEST, order = 1)
public class SignChainHandler implements IHandler<String, String> {
/**
* 處理加簽邏輯
* @param s
* @return
*/
@Override
public String handle(String s){
// 加簽邏輯
System.out.println("甲方爸爸要求加簽");
return "加簽";
}
}

6、具體怎么調用?這里我寫了個測試controller直接調用,具體如下:

/**
* @author 往事如風
* @version 1.0
* @date 2022/9/6 17:32
* @description
*/
@RestController
@Slf4j
public class TestController {

@Resource
private HandleChainManager handleChainManager;

@PostMapping("/send")
public String duty(@RequestBody String requestBody){
String response = handleChainManager.executeHandle(BusinessConstants.REQUEST, requestBody);
return response;
}
}

7、執行結果,會按照注解中標記的order依次執行。 

圖片

至此,完工。又可以開心的擼代碼了,然后在具體的執行類中,又是一頓if-else。。。

四、源碼中運用

4.1Mybatis源碼中的運用

Mybatis中的緩存接口Cache,cache作為一個緩存接口,最主要的功能就是添加和獲取緩存的功能,作為接口它有11個實現類,分別實現不同的功能,下面是接口源碼和實現類。

package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

public interface Cache {
String getId();

void putObject(Object var1, Object var2);

Object getObject(Object var1);

Object removeObject(Object var1);

void clear();

int getSize();

default ReadWriteLock getReadWriteLock(){
return null;
}
}

圖片

下面,我們來看下其中一個子類LoggingCache的源碼。主要看他的putObject方法和getObject方法,它在方法中直接傳給下一個實現去執行。這個實現類其實是為了在獲取緩存的時候打印緩存的命中率的。

public class LoggingCache implements Cache {
private final Log log;
private final Cache delegate;
protected int requests = 0;
protected int hits = 0;

public LoggingCache(Cache delegate){
this.delegate = delegate;
this.log = LogFactory.getLog(this.getId());
}

// ...
public void putObject(Object key, Object object){
this.delegate.putObject(key, object);
}

public Object getObject(Object key){
++this.requests;
Object value = this.delegate.getObject(key);
if (value != null) {
++this.hits;
}

if (this.log.isDebugEnabled()) {
this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
}

return value;
}
// ...
}

最后,經過Cache?接口各種實現類的處理,最終會到達PerpetualCache這個實現類。與之前的處理類不同的是,這個類中有一個map,在map中做存取,也就是說,最終緩存還是會保存在map中的。

public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap();

public PerpetualCache(String id){
this.id = id;
}

// ...

public void putObject(Object key, Object value){
this.cache.put(key, value);
}

public Object getObject(Object key){
return this.cache.get(key);
}
// ...

}

4.2spring源碼中的運用

4.2.1DispatcherServlet類

DispatcherServlet 核心方法 doDispatch。HandlerExecutionChain只是維護HandlerInterceptor的集合,可以向其中注冊相應的攔截器,本身不直接處理請求,將請求分配給責任鏈上注冊處理器執行,降低職責鏈本身與處理邏輯之間的耦合程度。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

4.2.2HandlerExecutionChain類

這里分析的幾個方法,都是從DispatcherServlet類的doDispatch方法中請求的。

  • 獲取攔截器,執行preHandle方法
boolean applyPreHandle(HttpServletRequest request, 
HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}

在applyPreHandle方法中,執行triggerAfterCompletion方法

void triggerAfterCompletion(HttpServletRequest request, 
HttpServletResponse response, Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}

獲取攔截器,執行applyPostHandle方法

void applyPostHandle(HttpServletRequest request, 
HttpServletResponse response, ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

五、總結

5.1 優點

  • 將請求與處理解耦。
  • 請求處理者(節點對象)只需要關注自己感興趣的請求進行處理即可,對于不感興趣的請求,轉發給下一個節點。
  • 具備鏈式傳遞處理請求功能,請求發送者無需知曉鏈路結構,只需等待請求處理結果。
  • 鏈路結構靈活,可以通過改變鏈路的結構動態的新增或刪減責任。
  • 易于擴展新的請求處理類(節點),符合開閉原則。

5.2 缺點

責任鏈太長或者處理時間過長,會影響整體性能。

如果節點對象存在循環引用時,會造成死循環,導致系統崩潰。

六、參考源碼

?編程文檔: https://gitee.com/cicadasmile/butte-java-note

應用倉庫: https://gitee.com/cicadasmile/butte-flyer-parent

責任編輯:武曉燕 來源: 知了一笑
相關推薦

2018-06-25 09:32:44

2023-05-15 08:38:58

模板方法模式

2022-11-26 00:00:06

裝飾者模式Component

2021-04-28 08:35:52

區塊鏈技術開發

2022-07-28 10:39:31

工業4.0供應鏈

2023-12-14 11:35:32

.NET泄露模式

2023-02-09 10:39:15

gRPC通信模式

2022-09-26 08:03:25

VMware虛擬機

2023-09-22 17:36:37

2021-01-28 22:31:33

分組密碼算法

2020-05-22 08:16:07

PONGPONXG-PON

2018-06-07 13:17:12

契約測試單元測試API測試

2019-02-13 14:15:59

Linux版本Fedora

2021-08-04 09:32:05

Typescript 技巧Partial

2021-01-29 08:32:21

數據結構數組

2021-02-06 08:34:49

函數memoize文檔

2022-08-08 08:25:21

Javajar 文件

2023-07-06 13:56:14

微軟Skype

2018-11-29 09:13:47

CPU中斷控制器

2020-10-15 06:56:51

MySQL排序
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区二区在线视频 | 国产精品久久久久久妇女 | 日本激情视频中文字幕 | 日韩色综合 | 亚洲综合在线网 | 国内久久 | 自拍偷拍亚洲视频 | 97精品超碰一区二区三区 | 欧美韩一区二区三区 | 欧美精品二区三区 | 色视频网站 | 免费精品久久久久久中文字幕 | 999久久久免费精品国产 | 亚洲欧美日韩精品久久亚洲区 | 欧美男人亚洲天堂 | 成人免费av在线 | 俺去俺来也www色官网cms | 91亚洲精品在线 | 国产精品一区二区三区在线 | 在线亚洲欧美 | 国产精品一区二区日韩 | 91最新在线视频 | 国产精品久久久久久亚洲调教 | 国产精品久久久久久久久久 | 欧美在线一二三 | 久久99精品久久久久久国产越南 | 欧美九九 | 国产精品大全 | 欧美操操操 | 久久视频精品在线 | 午夜精品久久久久久久久久久久久 | 青青久在线视频 | 国产一区免费视频 | 久久精彩视频 | 电影午夜精品一区二区三区 | 91亚洲国产成人精品一区二三 | 午夜视频在线 | 午夜在线 | 欧美在线一区二区三区 | 亚洲男人天堂av | 国产精品一区二区视频 |