摘要:責任鏈模式屬于行為型模式的一種,將請求沿著一條鏈傳遞,直到該鏈上的某個對象處理它為止。責任鏈模式通過將請求和處理分離開來,以進行解耦。源碼分析我們經常使用的就使用到了責任鏈模式,創建一個除了要在應用中做相應配置外,還需要實現接口。
概述責任鏈模式(Chain Of Responsibility Pattern)屬于行為型模式的一種,將請求沿著一條鏈傳遞,直到該鏈上的某個對象處理它為止。
定義如下:一個請求有多個對象來處理,這些對象形成一條鏈,根據條件確定具體由誰來處理,如果當前對象不能處理則傳遞給該鏈中的下一個對象,直到有對象處理它為止。責任鏈模式通過將請求和處理分離開來,以進行解耦。職責鏈模式結構的核心在于引入了一個抽象處理者。
UML結構圖
模式結構
Handler(抽象處理者): 定義一個處理請求的接口,提供對后續處理者的引用
ConcreteHandler(具體處理者): 抽象處理者的子類,處理用戶請求,可選將請求處理掉還是傳給下家;在具體處理者中可以訪問鏈中下一個對象,以便請求的轉發。
案例UML圖如下:
1.定義AbstractHandler(抽象處理者),使子類形成一條鏈
public abstract class AbstractHandler { private AbstractHandler handler; public abstract void handleRequest(String condition); public AbstractHandler getHandler() { return handler; } public void setHandler(AbstractHandler handler) { this.handler = handler; } }
2.創建若干個ConcreteHandler(具體處理者)繼承AbstractHandler,在當前處理者對象無法處理時,將執行權傳給下一個處理者對象
public class ConcreteHandlerA extends AbstractHandler { @Override public void handleRequest(String condition) { if (condition.equals("A")) { System.out.println("ConcreteHandlerA處理"); } else { System.out.println("ConcreteHandlerA不處理,由其他的Handler處理"); super.getHandler().handleRequest(condition); } } } public class ConcreteHandlerB extends AbstractHandler { @Override public void handleRequest(String condition) { if (condition.equals("B")) { System.out.println("ConcreteHandlerB處理"); } else { System.out.println("ConcreteHandlerB不處理,由其他的Handler處理"); super.getHandler().handleRequest(condition); } } } public class ConcreteHandlerZ extends AbstractHandler { @Override public void handleRequest(String condition) { //一般是最后一個處理者 System.out.println("ConcreteHandlerZ處理"); } }
3.創建ChainClient(測試類)
public class ChainClient { public static void main(String[] args) { AbstractHandler handlerA = new ConcreteHandlerA(); AbstractHandler handlerB = new ConcreteHandlerB(); AbstractHandler handlerZ = new ConcreteHandlerZ(); // 如A處理不掉轉交給B handlerA.setHandler(handlerB); handlerB.setHandler(handlerZ); handlerA.handleRequest("Z"); } }
4.運行效果
----------------------handleRequest("A")------------------------- ConcreteHandlerA處理 ----------------------handleRequest("B")------------------------- ConcreteHandlerA不處理,由其他的Handler處理 ConcreteHandlerB處理 ----------------------handleRequest("Z")------------------------- ConcreteHandlerA不處理,由其他的Handler處理 ConcreteHandlerB不處理,由其他的Handler處理 ConcreteHandlerZ處理
可以看出,客戶端創建了三個處理者對象,并指定第一個處理者對象的下家是第二個處理者對象,第二個處理者對象的下家是第三個處理者對象。然后客戶端將請求傳遞給第一個處理者對象。
由于本示例的傳遞邏輯非常簡單:只要有下家,就傳給下家處理;如果沒有下家,就自行處理。因此,第一個處理者對象接到請求后,會將請求傳遞給第二個處理者對象。由于第二個處理者對象沒有下家,于是自行處理請求。活動時序圖如下所示。
純與不純純:要么承擔全部責任,要么將責任推給下家,不允許出現某一個具體處理者對象在承擔了一部分或全部責任后又將責任向下傳遞的情況。
不純:允許某個請求被一個具體處理者部分處理后再向下傳遞,或者一個具體處理者處理完某請求后其后繼處理者可以繼續處理該請求,而且一個請求可以最終不被任何處理者對象所接收。
Filter源碼分析我們經常使用的Filter就使用到了責任鏈模式,創建一個Filter除了要在應用中做相應配置外,還需要實現javax.servlet.Filter接口。
@Configuration public class MyFilter implements Filter { private final static Logger LOGGER = LoggerFactory.getLogger(MyFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { LOGGER.info("init"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { LOGGER.info("doFilter"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { LOGGER.info("destroy"); } }
使用DEBUG模式所看到的結果如下:
Filter 集
在源碼ApplicationFilterChain中,定義了一個ApplicationFilterConfig的數組來存放所有的Filter,使之形成鏈狀
public final class ApplicationFilterChain implements FilterChain { // 擴容規則 public static final int INCREMENT = 10; // Filter集,默認大小為 0 個 private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; }
ApplicationFilterConfig 裝載規則
在應用首次啟動時,會自動實例化對象,并從web應用中讀取配置的Filter的信息,然后裝進該容器。
public final class ApplicationFilterConfig implements FilterConfig, Serializable { // 省略代碼... }
ApplicationFilterConfig 擴容規則
//將過濾器添加到在此鏈條中執行的過濾器集合中。 void addFilter(ApplicationFilterConfig filterConfig) { // 防止多次添加相同的過濾器 for(ApplicationFilterConfig filter:filters) if(filter==filterConfig) return; if (n == filters.length) { // 定義一個在原基礎之上 +10 的新數組 ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT]; // 將源數組的元素拷貝到目標數組中去 System.arraycopy(filters, 0, newFilters, 0, n); // 重新賦值 filters = newFilters; } //將變量filterConfig放入ApplicationFilterConfig數組中,并將當前過濾器鏈里面擁有的過濾器數目+1 filters[n++] = filterConfig; }
addFilter 的使用
public static ApplicationFilterChain createFilterChain(ServletRequest request,Wrapper wrapper, Servlet servlet) { //如果沒有 servlet 要執行,則返回null if (servlet == null) return null; // 創建和初始化過濾器鏈對象 ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; if (Globals.IS_SECURITY_ENABLED) { // 為了安全起見:不要回收 filterChain = new ApplicationFilterChain(); } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { // 調度程序在使用中 filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported()); // 獲取過濾器上下文映射 StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); // 如果沒有過濾器映射,就認為當前執行完成 if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // 獲取匹配的過濾器映射信息 DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR); String requestPath = null; Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR); if (attribute != null){ requestPath = attribute.toString(); } String servletName = wrapper.getName(); // 將相關路徑映射的過濾器添加到此過濾器鏈中 for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // 添加與servlet名稱匹配的篩選器 for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // 返回完整的過濾器鏈 return filterChain; }
createFilterChain 的調用
final class StandardWrapperValve extends ValveBase { public final void invoke(Request request, Response response) throws IOException, ServletException { // 省略代碼... // 為這個請求創建過濾器鏈 ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); // 省略代碼... filterChain.doFilter(request.getRequest(), response.getResponse()); } }
執行流程
在StandardWrapperValue類的invoke()方法中調用ApplicationFilterChai類的createFilterChain()方法
在ApplicationFilterChai類的createFilterChain()方法中調用ApplicationFilterChain類的addFilter()方法
在ApplicationFilterChain類的addFilter()方法中給ApplicationFilterConfig數組賦值。
最后一步
在doFilter()方法中最后會調用一個internalDoFilter()方法,目的就是執行ApplicationFilterChain中的全部過濾器,從代碼中可以發現它調用了doFilter,而在doFilter又會調用internalDoFilter 從而使所有的Filter都得以調用
private void internalDoFilter(ServletRequest request,ServletResponse response) throws IOException, ServletException { // 如果存在下一個,繼續調用下一個過濾器 if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { // 此處調用Filter的doFilter()方法 / 而 doFilter 又會調用 internalDoFilter 直到調用完所有的過濾器 filter.doFilter(request, response, this); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.filter"), e); } return; } // 從最后一個開始調用 try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } if (request.isAsyncSupported() && !servletSupportsAsync) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } // 包裝請求 if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); } else { servlet.service(request, response); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.servlet"), e); } finally { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(null); lastServicedResponse.set(null); } } }總結
職責鏈模式通過建立一條鏈來組織請求的處理者,請求將沿著鏈進行傳遞,請求發送者無須知道請求在何時、何處以及如何被處理,實現了請求發送者與處理者的解耦。在軟件開發中,如果遇到有多個對象可以處理同一請求時可以應用職責鏈模式,例如在Web應用開發中創建一個過濾器(Filter)鏈來對請求數據進行過濾,在工作流系統中實現公文的分級審批等等,使用職責鏈模式可以較好地解決此類問題。
優點
降低耦合度,分離了請求與處理,無須知道是哪個對象處理其請求
簡化對象的相互連接,僅保持一個指向后者的引用,而不需保持所有候選接受者的引用
擴展容易,新增具體請求處理者,只需要在客戶端重新建鏈即可,無需破壞原代碼
缺點
如果請求沒有明確的接收者,那么就不能保證它一定會被處理,該請求可能一直到鏈的末端都得不到處理;一個請求也可能因職責鏈沒有被正確配置而得不到處理
較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響,而且在進行代碼調試時不太方便。
如果建鏈不當,可能會造成循環調用,將導致系統陷入死循環。
金無足赤,人無完人。就像所有的設計模式一樣,有優點優缺點,但是總的來說優點必定大于缺點或者說缺點相對于優點來說更可控。
說點什么參考文獻:http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html
全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter11/battcn-chain-responsibility
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
福利關注公眾號:battcn,回復springboot即可獲得
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70629.html
摘要:面向對象常見的設計模式有策略模式模板方法觀察者模式責任鏈模式以及工廠模式,使用表達式函數式編程思維有助于避免面向對象開發中的那些固定代碼。 本文是一篇《Java 8實戰》的閱讀筆記,閱讀大約需要5分鐘。 有點標題黨,但是這確實是我最近使用Lambda表達式的感受。設計模式是過去的一些好的經驗和套路的總結,但是好的語言特性可以讓開發者不去考慮這些設計模式。面向對象常見的設計模式有策略模式...
摘要:前言近期在做的攔截器功能,正好用到了責任鏈模式。通過官方圖就可以非常清楚的看出是一個責任鏈模式用責任鏈模式設計一個攔截器對于攔截器來說使用責任鏈模式再好不過了。設置攔截器到責任鏈中時通過反射將的值保存到各個攔截器中。 showImg(https://segmentfault.com/img/remote/1460000016756077?w=1733&h=1300); 前言 近期在做 ...
摘要:推文用設計模式解構三國是一種什么體驗行為型設計模式一策略模式工廠模式優化結構狀態模式隨著狀態改變而改變行為。推文狀態機與狀態模式責任鏈模式多個對象依次處理請求前者指定后者。代理模式代理針對一個對象,為了增加控制等中介雙方都是多個,為了解耦。 策略模式 選擇使用封裝好的一系列算法,可相互替換。 類比:商店[Context]買完衣服買單[Stratege](現金[Concrete Stra...
摘要:迭代器模式屬于行為型模式的一種,提供一種方法訪問一個容器中各個元素,而又不需要暴露該對象的內部細節。迭代器模式把在元素之間游走的責任交給迭代器,而不是聚合對象。 迭代器模式(Iterator Pattern)屬于行為型模式的一種,提供一種方法訪問一個容器中各個元素,而又不需要暴露該對象的內部細節。 概述 迭代器模式聽起來可能感覺很陌生,但是實際上,迭代器模式是所有設計模式中最簡單也是...
閱讀 2952·2023-04-26 01:52
閱讀 3478·2021-09-04 16:40
閱讀 3636·2021-08-31 09:41
閱讀 1771·2021-08-09 13:41
閱讀 570·2019-08-30 15:54
閱讀 2969·2019-08-30 11:22
閱讀 1622·2019-08-30 10:52
閱讀 955·2019-08-29 13:24