深入了解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)求和處理分開、實(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-06Java中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-01Springboot 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-12Spring?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