本文內容思惟導圖以下:java
責任鏈模式定義:爲請求建立一個處理此請求對象的鏈。json
適用場景(核心):只要把你的請求拋給第一個處理者,不用關心誰處理的,而且最終會返回你一個結果。設計模式
優勢:請求者和處理者解耦,請求者不用知道誰處理的,處理者能夠不用知道請求的全貌。數組
缺點:每一個請求從鏈頭遍歷到鏈尾,影響性能。代碼調試時候不方便。app
類型:行爲型。ide
類圖:性能
源碼中的典型應用:測試
員工在OA系統中提交請假申請,首先項目經理處理,他能審批3天之內的假期,若是大於3天,則由項目經理則轉交給總經理處理。接下來咱們用責任鏈模式實現這個過程。this
一、封裝請假信息實體類spa
public class LeaveRequest { private String name; // 請假人姓名 private int numOfDays; // 請假天數 private int workingAge; //員工工齡(在公司大於2年則總經理會審批) //省略get..set.. }
二、抽象處理者類 Handler,維護一個nextHandler屬性,該屬性爲當前處理者的下一個處理者的引用;聲明瞭抽象方法process,其實在這裏也用了方法模板模式:
public abstract class ApproveHandler { protected ApproveHandler nextHandler;//下一個處理者(與類一致,這段代碼很重要) public void setNextHandler(ApproveHandler approveHandler){ this.nextHandler=approveHandler; } public abstract void process(LeaveRequest leaveRequest); // 處理請假(這裏用了模板方法模式) }
三、項目經理處理者,能處理小於3天的假期,而請假信息裏沒有名字時,審批不經過:
public class PMHandler extends ApproveHandler{ @Override public void process(LeaveRequest leaveRequest) { //未填寫姓名的請假單不經過 if(null != leaveRequest.getName()){ if(leaveRequest.getNumOfDays() <= 3){ System.out.println(leaveRequest.getName()+",你經過項目經理審批!"); }else { System.out.println("項目經理轉交總經理"); if(null != nextHandler){ nextHandler.process(leaveRequest); } } }else { System.out.println("請假單未填寫完整,未經過項目經理審批!"); return; } } }
四、總經理處理者,能處理大於3天的假期,且工齡超過2年纔會審批經過:
public class GMHandler extends ApproveHandler{ @Override public void process(LeaveRequest leaveRequest) { //員工在公司工齡超過2年,則審批經過 if(leaveRequest.getWorkingAge() >=2 && leaveRequest.getNumOfDays() > 3){ System.out.println(leaveRequest.getName()+",你經過總經理審批!"); if(null != nextHandler){ nextHandler.process(leaveRequest); } }else { System.out.println("在公司年限不夠,長假未經過總經理審批!"); return; } } }
實例代碼完成,咱們測試一下:
public class Test { public static void main(String[] args) { PMHandler pm = new PMHandler(); GMHandler gm = new GMHandler(); LeaveRequest leaveRequest = new LeaveRequest(); leaveRequest.setName("張三"); leaveRequest.setNumOfDays(4);//請假4天 leaveRequest.setWorkingAge(3);//工齡3年 pm.setNextHandler(gm);//設置傳遞順序 pm.process(leaveRequest); } }
運行結果:
------
項目經理轉交總經理
張三,你經過總經理審批!
------
Filter接口有很是多的實現類,這裏挑選doFilter方法中的FilterChain參數來看,Tomcat和SpringSecurity中都用到責任鏈模式:
進入第一個,過濾器鏈 ApplicationFilterChain 的關鍵代碼以下,過濾器鏈實際是一個 ApplicationFilterConfig 數組:
final class ApplicationFilterChain implements FilterChain, CometFilterChain { private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; // 過濾器鏈 private Servlet servlet = null; // 目標 // ... @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { // ... } else { internalDoFilter(request,response); // 調用 internalDoFilter 方法 } } private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { // 從過濾器數組中取出當前過濾器配置,而後下標自增1 ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { filter = filterConfig.getFilter(); // 從過濾器配置中取出該 過濾器對象 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 { // 調用過濾器的 doFilter,完成一個過濾器的過濾功能 filter.doFilter(request, response, this); } return; // 這裏很重要,不會重複執行後面的 servlet.service(request, response) } // 執行完過濾器鏈的全部過濾器以後,調用 Servlet 的 service 完成請求的處理 if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { if( Globals.IS_SECURITY_ENABLED ) { } else { servlet.service(request, response); } } else { servlet.service(request, response); } } // 省略... }
這裏能夠看出ApplicationFilterChain類扮演了抽象處理者角色,doFilter就相似於剛纔請假流程裏的process方法。
當下標小於過濾器數組長度 n 時,也就是過濾器鏈未執行完,從數組中取出並調用當前過濾器的 doFilter方法 ,若是下標一直小於n,則循環調用doFilter方法經過嵌套遞歸的方式來串成一條鏈。
當最後的過濾器執行完畢,也就是走到最後一個return;時,結束遞歸調用doFilter。if (pos < n) 爲false,調用後面的servlet.service(request, response) 方法。return;這一點在請假流程裏也有體現。
參考: