深入了解Java設(shè)計(jì)模式之職責(zé)鏈模式
定義
使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系,將這個(gè)對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。

解決的問(wèn)題
請(qǐng)求和處理分開(kāi)、實(shí)現(xiàn)解耦、提高系統(tǒng)的靈活性
可以動(dòng)態(tài)的調(diào)整請(qǐng)求的鏈條,增加系統(tǒng)靈活性
核心要點(diǎn)
職責(zé)鏈模式每個(gè)執(zhí)行者都包含了另一個(gè)執(zhí)行者的引用。如果一個(gè)對(duì)象不能處理該請(qǐng)求,會(huì)把請(qǐng)求傳遞給下一個(gè)執(zhí)行者。
客戶端需要?jiǎng)討B(tài)的調(diào)整,執(zhí)行者的上下級(jí)。
Handler 里面聚合它自己,在 HandlerRequest 里判斷是否合適,如果沒(méi)達(dá)到條件則向下傳遞,向誰(shuí)傳遞之前 set 進(jìn)去。
類圖

代碼實(shí)現(xiàn)
抽象父類
/**
* 處理請(qǐng)求的抽象類
*
* @author Promsing(張有博)
* @version 1.0.0
* @since 2022/9/7 - 9:29
*/
public abstract class Handler {
//維持一個(gè)下一個(gè)執(zhí)行者的引用
protected Handler handler;
protected void setHandler(Handler handler) {
this.handler = handler;
}
//處理請(qǐng)求的抽象方法
public abstract void processRequest(int request);
}
執(zhí)行者-三個(gè)
/**
* 只處理數(shù)字1-10
*
* @author Promsing(張有博)
* @version 1.0.0
* @since 2022/9/7 - 9:31
*/
public class ConcreteHandlerA extends Handler{
@Override
public void processRequest(int request) {
if (request>=0 && request<10 ){
System.out.println("ConcreteHandlerA已經(jīng)處理完畢了 "+request);
return;
}
if (handler!=null){
//下一位處理
handler.processRequest(request);
}
}
}
/**
* 只處理數(shù)字10-20
*
* @author Promsing(張有博)
* @version 1.0.0
* @since 2022/9/7 - 9:31
*/
public class ConcreteHandlerB extends Handler{
@Override
public void processRequest(int request) {
if (request>=10 && request<20 ){
System.out.println("ConcreteHandlerB已經(jīng)處理完畢了 "+request);
return;
}
if (handler!=null){
//下一位處理
handler.processRequest(request);
}
}
}
/**
*只處理數(shù)字30+
*
* @author Promsing(張有博)
* @version 1.0.0
* @since 2022/9/7 - 9:31
*/
public class ConcreteHandlerC extends Handler{
@Override
public void processRequest(int request) {
if (request>=30 ){
System.out.println("ConcreteHandlerC已經(jīng)處理完畢了 "+request);
return;
}
if (handler!=null){
//下一位處理
handler.processRequest(request);
}
}
}
客戶端
public class Main {
public static void main(String[] args) {
//創(chuàng)建執(zhí)行者
Handler h1=new ConcreteHandlerA();
Handler h2=new ConcreteHandlerB();
Handler h3=new ConcreteHandlerC();
//設(shè)置向下級(jí)的順序,可根據(jù)配置動(dòng)態(tài)設(shè)置上下級(jí)
h1.setHandler(h2);
h2.setHandler(h3);
int[] requests={8,11,23,50,7,19,28,40};
//循環(huán)處理請(qǐng)求,不同的數(shù)值,交給不同的執(zhí)行者
for (int request : requests) {
h1.processRequest(request);
}
}
}
拓展
SpringMVC中DispatchServlet使用職責(zé)鏈
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;//請(qǐng)求
HandlerExecutionChain mappedHandler = null;//執(zhí)行鏈
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest); //根據(jù)請(qǐng)求-獲得執(zhí)行鏈
//判斷mappedHandler是否為空,空:404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 確定當(dāng)前請(qǐng)求的處理程序適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//職責(zé)鏈的前置攔截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//執(zhí)行handler方法-controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//職責(zé)鏈的后置攔截
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//對(duì)結(jié)果集進(jìn)行處理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//執(zhí)行完成了攔截器
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
finally {
//資源釋放
}
}
攔截器的方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors(); //獲得攔截器
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {//循環(huán)遍歷執(zhí)行前置攔截
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors(); //獲得攔截器
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) { //循環(huán)遍歷執(zhí)行后置攔截
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors(); //獲得攔截器
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try { //循環(huán)遍歷執(zhí)行完成攔截
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
應(yīng)用場(chǎng)景
1、有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求由運(yùn)行時(shí)刻自動(dòng)確定。
2、在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。
3、可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求。
4、比如:JS 中的事件冒泡,JAVA WEB 中 Apache Tomcat 對(duì) Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。
到此這篇關(guān)于深入了解Java設(shè)計(jì)模式之職責(zé)鏈模式的文章就介紹到這了,更多相關(guān)Java職責(zé)鏈模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中轉(zhuǎn)換器設(shè)計(jì)模式深入講解
這篇文章主要給大家介紹了關(guān)于Java中轉(zhuǎn)換器設(shè)計(jì)模式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
使用MockMvc進(jìn)行controller層單元測(cè)試 事務(wù)自動(dòng)回滾的完整案例
這篇文章主要介紹了使用MockMvc進(jìn)行controller層單元測(cè)試 事務(wù)自動(dòng)回滾的完整案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java中xxl-job實(shí)現(xiàn)分片廣播任務(wù)的示例
本文主要介紹了Java中xxl-job實(shí)現(xiàn)分片廣播任務(wù)的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
基于springboot 長(zhǎng)輪詢的實(shí)現(xiàn)操作
這篇文章主要介紹了基于springboot 長(zhǎng)輪詢的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
Springboot Mybatis-Plus數(shù)據(jù)庫(kù)單元測(cè)試實(shí)戰(zhàn)(三種方式)
這篇文章主要介紹了Springboot Mybatis-Plus數(shù)據(jù)庫(kù)單元測(cè)試實(shí)戰(zhàn)(三種方式),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Spring?Boot?3中一套可以直接用于生產(chǎn)環(huán)境的Log4J2日志配置詳解
Log4J2是Apache Log4j的升級(jí)版,參考了logback的一些優(yōu)秀的設(shè)計(jì),并且修復(fù)了一些問(wèn)題,因此帶來(lái)了一些重大的提升,這篇文章主要介紹了Spring?Boot?3中一套可以直接用于生產(chǎn)環(huán)境的Log4J2日志配置,需要的朋友可以參考下2023-12-12

