責任鏈模式(Chain Of Responsibility Design Pattern),也叫做職責鏈,是將請求的傳送和接收解耦,讓多個接收物件都有機會處理這個請求。當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止。
一、責任鏈模式介紹
其實在日常生活中,有不少使用責任鏈的場景。比如公司採購審批流程,需要各個部門領導的批准同意。在責任鏈模式中,客戶端只需要將請求傳送到責任鏈上,無須關心請求的處理細節和傳遞,請求會自動進行傳遞。
1.1 責任鏈模式的結構
責任鏈模式的結構大致如下所示:
abstract Handler
:抽象處理者,定義一個處理請求的介面,內部包含抽象處理方法和後繼具體處理者Handler1、Handler2
:具體處理者,具體實現抽象處理者的方法,並對請求做一些邏輯處理Client
:客戶端,使用職責鏈模式
1.2 責任鏈模式的實現
根據上面的類圖,可以實現如下程式碼:
/**
* @description: 抽象處理類
* @author: wjw
* @date: 2022/4/4
*/
public abstract class Handler {
private Handler successor;
public Handler getSuccessor() {
return successor;
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 處理請求的抽象方法
* @param request 請求
*/
public abstract void handleRequest(String request);
}
/**
* @description: 具體處理者1
* @author: wjw
* @date: 2022/4/4
*/
public class Handler1 extends Handler{
private String handler;
public Handler1(String handler) {
this.handler = handler;
}
@Override
public void handleRequest(String request) {
if ("handler1".equals(request)) {
System.out.println("具體處理者handler1進行請求處理");
} else {
if (getSuccessor() != null) {
//如果指向下一個具體處理者
getSuccessor().handleRequest(request);
} else {
System.out.println("沒有處理者進行處理");
}
}
}
}
/**
* @description: 具體處理者2
* @author: wjw
* @date: 2022/4/4
*/
public class Handler2 extends Handler{
private String handler;
public Handler2(String handler) {
this.handler = handler;
}
@Override
public void handleRequest(String request) {
if ("handler2".equals(request)) {
System.out.println("具體處理者handler2進行請求處理");
} else {
if (getSuccessor() != null) {
getSuccessor().handleRequest(request);
} else {
System.out.println("請求沒有被任何處理者處理");
}
}
}
}
/**
* @description: 客戶端類
* @author: wjw
* @date: 2022/4/4
*/
public class Client {
public static void main(String[] args) {
Handler handler1 = new Handler1("handler1");
Handler handler2 = new Handler2("handler2");
handler1.setSuccessor(handler2);
handler1.handleRequest("handler1");
handler1.handleRequest("handler2");
}
}
測試結果:
具體處理者handler1進行請求處理
具體處理者handler2進行請求處理
二、責任鏈模式的應用場景
職責鏈模式最常見的應用就是用來開發各種框架的過濾器和攔截器,比如Spring Interceptor和Servlet Filter
2.1 在Servlet Filter中的應用
Filter 可以實現對HTTP 請求的過濾功能,比如鑑權、限流、記錄日誌、驗證引數等等。比如一些Servlet 容器(TomCat、Jetty等)就支援Filter的過濾功能。以TomCat 為例:
所以當Servlet 請求到來時,首先會經過Filter 處理,最後再到達Servlet例項。我這裡選取的TomCat版本是SpringBoot自帶的9.0,先來看看FilterChain
的介面:
public interface FilterChain {
//Filter具體實現
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain
是責任鏈模式的具體實現類:
public final class ApplicationFilterChain implements FilterChain {
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; //Filter配置陣列
private int pos = 0; //執行Filter的序號
private int n = 0; //目前Filter的個數
private Servlet servlet = null;
public ApplicationFilterChain() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
//Filter 具體實現
//...
}
public static ServletRequest getLastServicedRequest() {
return (ServletRequest)lastServicedRequest.get();
}
public static ServletResponse getLastServicedResponse() {
return (ServletResponse)lastServicedResponse.get();
}
void addFilter(ApplicationFilterConfig filterConfig) {
ApplicationFilterConfig[] newFilters = this.filters;
int var3 = newFilters.length;
for(int var4 = 0; var4 < var3; ++var4) {
ApplicationFilterConfig filter = newFilters[var4];
if (filter == filterConfig) {
return;
}
}
//增加Filter
if (this.n == this.filters.length) {
newFilters = new ApplicationFilterConfig[this.n + 10];
System.arraycopy(this.filters, 0, newFilters, 0, this.n);
this.filters = newFilters;
}
this.filters[this.n++] = filterConfig;
}
}
2.2 在Spring Interceptor中的應用
和Servlet Filter類似,在Spring 中也有對應的過濾器 Interceptor。它是由Spring MVC 框架來實現,借一張來自《設計模式之美》的圖片來說明:
客戶端傳送請求,首先會經過Servlet Filter,然後再經過Spring Interceptor,最後再到達具體的業務中。
和Filter一樣,Interceptor 中也是基於責任鏈模式來實現的,與之相對的HandlerInterceptor
是抽象處理介面:
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
會有各種具體處理類的實現:
此外在其他框架中也存在著職責鏈模式的使用,比如Dubbo Filter、Netty ChannelPipeline等等
三、責任鏈模式實戰
模擬在618大促期間的業務系統上線審批流程場景:
像是這些⼀線電商類的互聯⽹公司,阿⾥、京東、拼多多等,在618期間都會做⼀些運營活動場景以及 提供的擴容備戰,就像過年期間百度的紅包⼀樣。但是所有開發的這些系統都需要陸續的上線,因為臨 近618有時候也有⼀些緊急的調整的需要上線,但為了保障線上系統的穩定性是儘可能的減少上線的, 也會相應的增強審批⼒度。就像⼀級響應、⼆級響應⼀樣。
⽽這審批的過程在隨著特定時間點會增加不同級別的負責⼈加⼊,每個⼈就像責任鏈模式中的每⼀個核 ⼼點。對於研發並不需要關⼼具體的審批流程處理細節,只需要知道這個上線更嚴格,級別也更 ⾼,但對於研發⼈員來說同樣是點選相同的提審按鈕,等待稽核。
使用責任鏈模式可以將各個服務模組按照一、二、三級進行分離,每個級別可以像Filter
一樣用Successor
下一個級別的方法進行呼叫。具體結構圖如下(來自《重學Java設計模式》)
具體程式碼如下:
參考資料
《設計模式之美》
http://c.biancheng.net/view/4024.html
《重學Java設計模式》