前言
什么是責(zé)任鏈
責(zé)任鏈?zhǔn)且环N設(shè)計模式阱佛,它讓多個對象有機(jī)會處理同一個請求,這些對象形成一個鏈缚柏。請求從鏈的一端開始,逐個傳遞給鏈上的對象碟贾,直到某個對象處理它或者請求未被處理币喧。這樣,發(fā)送請求者無需知道哪個對象會處理袱耽,實現(xiàn)了發(fā)送者與接收者的解耦杀餐,增加了系統(tǒng)的靈活性
責(zé)任鏈的常用場景
- 權(quán)限與認(rèn)證系統(tǒng):在登錄認(rèn)證、權(quán)限驗證流程中朱巨,不同的處理者可以檢查用戶名密碼怜浅、驗證權(quán)限級別、處理單點登錄等蔬崩。請求(如訪問資源)沿著責(zé)任鏈傳遞恶座,直到找到合適的處理器來授權(quán)或拒絕訪問。
-
日志記錄與錯誤處理:根據(jù)日志級別(如DEBUG, INFO, WARNING,
ERROR)或錯誤類型沥阳,不同的處理器負(fù)責(zé)記錄或處理相應(yīng)的日志信息或異常跨琳。開發(fā)者可以靈活地插入或移除處理邏輯,而不影響其他日志處理桐罕。 - 請求過濾與處理:在Web服務(wù)或API網(wǎng)關(guān)中脉让,責(zé)任鏈可用于實現(xiàn)一系列預(yù)處理任務(wù),如參數(shù)校驗功炮、請求限流溅潜、IP黑名單過濾、會話管理等薪伏,每個處理環(huán)節(jié)關(guān)注于特定的驗證或轉(zhuǎn)換邏輯滚澜。
- 命令與事件處理:在處理一系列可選的或有順序依賴的命令或事件時,責(zé)任鏈允許按照預(yù)定的邏輯順序嘗試執(zhí)行處理邏輯嫁怀,直到命令被執(zhí)行或事件被妥善處理设捐。
- 工作流與審批流程:在企業(yè)應(yīng)用中借浊,審批流程可以設(shè)計成責(zé)任鏈,每個節(jié)點代表一個審批層級或角色萝招,請求(如報銷單)依次經(jīng)過各個審批節(jié)點蚂斤,直至最終批準(zhǔn)或拒絕。
- UI事件處理:在圖形界面應(yīng)用程序中槐沼,事件(如鼠標(biāo)點擊曙蒸、鍵盤輸入)可以通過責(zé)任鏈分發(fā)給不同的組件,每個組件決定是否消費此事件岗钩,未處理的事件繼續(xù)傳遞給鏈中的下一個組件逸爵。
今天給大家的介紹的責(zé)任鏈有點特殊,它是基于Pipeline-Valve模型凹嘲,這種模型跟常規(guī)的責(zé)任鏈模式有點區(qū)別
- 每個Pipeline都是有特定的Valve师倔,而且是在管道的最后一個執(zhí)行,這個Valve叫BaseValve周蹭,并且BaseValve是不可刪除的趋艘;
- 在上層容器的管道的BaseValve中會調(diào)用下層容器的管道。
示例圖:
如何實現(xiàn)Pipeline-Valve模型
1凶朗、定義Valve接口
該接口表示處理鏈中的單個處理單元瓷胧。
public interface Valve extends Ordered {
Valve getNextValve();
void setNextValve(Valve next);
void invoke(ValveContext context);
default boolean isBaiscValve() {
return false;
}
}
2、定義抽象valve(可選)
注:定義該抽象valve主要是為了復(fù)用
public abstract class AbstractValve implements Valve {
protected Valve nextValve;
@Override
public Valve getNextValve() {
return nextValve;
}
@Override
public void setNextValve(Valve next) {
this.nextValve = next;
}
@Override
public void invoke(ValveContext context) {
doInvoke(context);
if(nextValve!=null){
nextValve.invoke(context);
}
}
public abstract void doInvoke(ValveContext context);
}
3棚愤、 定義Pipeline接口
該接口主要用來用于管理Valve的集合搓萧,并提供方法來添加Valve、設(shè)置特定valve以及啟動處理流程宛畦。
public interface Pipeline {
void setBasic(Valve valve);
void addValve(Valve valve);
void process(ValveContext context);
}
4瘸洛、定義Pipeline的默認(rèn)實現(xiàn)
public class StandardPipeline implements Pipeline {
/**
* 第一個閥門
*/
protected Valve first;
/**
* 最后一個閥門
*/
protected Valve basic;
@Override
public void setBasic(Valve valve) {
validateValve(valve,true);
this.basic = valve;
}
@Override
public void addValve(Valve valve) {
validateValve(valve,false);
if(first == null){
this.first = valve;
valve.setNextValve(basic);
}else{
Valve current = first;
while(current != null){
if(current.getNextValve() == basic){
current.setNextValve(valve);
valve.setNextValve(basic);
break;
}
current = current.getNextValve();
}
}
}
@Override
public void process(ValveContext context) {
if(first != null){
if(context == null){
context = new ValveContext();
}
first.invoke(context);
}
}
public void validateValve(Valve valve,boolean isCheckBasicValve){
Assert.notNull(valve, "valve must not be null");
if(isCheckBasicValve){
Assert.isTrue(valve.isBaiscValve(), "valve must be basic valve");
}
}
}
注: addValue else流程,是為了保證basic閥門一定是在流程最后被調(diào)用
5次和、定義具體valve
@Component
public class FirstValve extends AbstractValve {
@Override
public void doInvoke(ValveContext context) {
String requestId = "lybgeek-" + UUID.randomUUID().toString();
System.out.println("第一道閥門: requestId-->【" + requestId + "】");
Map<String,Object> request = context.getRequest();
request.put("source",FirstValve.class.getSimpleName());
context.setRequest(request);
context.setRequestId(requestId);
}
@Override
public int getOrder() {
return 1;
}
}
其他閥門類似反肋,就不列舉了
6、通過Pipeline 驅(qū)動valve
public class PipelineMainTest {
public static void main(String[] args) {
Pipeline pipeline = new StandardPipeline();
pipeline.setBasic(new BasicValve());
pipeline.addValve(new FirstValve());
pipeline.addValve(new SecondValve());
pipeline.addValve(new ThirdValve());
pipeline.process(new ValveContext());
}
}
總結(jié)
如果大家對tomcat有了解的話踏施,就會知道本文的實現(xiàn)其實就是tomcat的pipeline-valve的簡化版實現(xiàn)石蔗。其次上文pipeline驅(qū)動valve的步驟可以托管給spring,文末demo鏈接的代碼畅形,有做了相應(yīng)實現(xiàn)养距,感興趣的朋友,可以點擊文末鏈接查看
demo鏈接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-pipeline-valve