在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責任鏈(Chain of Responsibility)模式的:java
責任鏈模式是一種對象的行爲模式。在責任鏈模式裏,不少對象由每個對象對其下家的引用而鏈接起來造成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態地從新組織和分配責任。web
擊鼓傳花是一種熱鬧而又緊張的飲酒遊戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,若是花束在某人手中,則該人就得飲酒。數組
好比說,賈母、賈赦、賈政、賈寶玉和賈環是五個參加擊鼓傳花遊戲的傳花者,他們組成一個環鏈。擊鼓者將花傳給賈母,開始傳花遊戲。花由賈母傳給賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環,由賈環傳回給賈母,如此往復,以下圖所示。當鼓聲中止時,手中有花的人就得執行酒令。app
擊鼓傳花即是責任鏈模式的應用。責任鏈多是一條直線、一個環鏈或者一個樹結構的一部分。ide
下面使用了一個責任鏈模式的最簡單的實現。測試
責任鏈模式涉及到的角色以下所示:ui
● 抽象處理者(Handler)角色:定義出一個處理請求的接口。若是須要,接口能夠定義 出一個方法以設定和返回對下家的引用。這個角色一般由一個Java抽象類或者Java接口實現。上圖中Handler類的聚合關係給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操做。this
● 具體處理者(ConcreteHandler)角色:具體處理者接到請求後,能夠選擇將請求處理掉,或者將請求傳給下家。因爲具體處理者持有對下家的引用,所以,若是須要,具體處理者能夠訪問下家。spa
抽象處理者角色指針
public abstract class Handler { /** * 持有後繼的責任對象 */ protected Handler successor; /** * 示意處理請求的方法,雖然這個示意方法是沒有傳入參數的 * 但實際是能夠傳入參數的,根據具體須要來選擇是否傳遞參數 */ public abstract void handleRequest(); /** * 取值方法 */ public Handler getSuccessor() { return successor; } /** * 賦值方法,設置後繼的責任對象 */ public void setSuccessor(Handler successor) { this.successor = successor; } }
具體處理者角色
public class ConcreteHandler extends Handler { /** * 處理方法,調用此方法處理請求 */ @Override public void handleRequest() { /** * 判斷是否有後繼的責任對象 * 若是有,就轉發請求給後繼的責任對象 * 若是沒有,則處理請求 */ if(getSuccessor() != null) { System.out.println("放過請求"); getSuccessor().handleRequest(); }else { System.out.println("處理請求"); } } }
客戶端類
public class Client { public static void main(String[] args) { //組裝責任鏈 Handler handler1 = new ConcreteHandler(); Handler handler2 = new ConcreteHandler(); handler1.setSuccessor(handler2); //提交請求 handler1.handleRequest(); } }
能夠看出,客戶端建立了兩個處理者對象,並指定第一個處理者對象的下家是第二個處理者對象,而第二個處理者對象沒有下家。而後客戶端將請求傳遞給第一個處理者對象。
因爲本示例的傳遞邏輯很是簡單:只要有下家,就傳給下家處理;若是沒有下家,就自行處理。所以,第一個處理者對象接到請求後,會將請求傳遞給第二個處理者對象。因爲第二個處理者對象沒有下家,因而自行處理請求。活動時序圖以下所示。
來考慮這樣一個功能:申請聚餐費用的管理。
不少公司都是這樣的福利,就是項目組或者是部門能夠向公司申請一些聚餐費用,用於組織項目組成員或者是部門成員進行聚餐活動。
申請聚餐費用的大體流程通常是:由申請人先填寫申請單,而後交給領導審批,若是申請批准下來,領導會通知申請人審批經過,而後申請人去財務領取費用,若是沒有批准下來,領導會通知申請人審批未經過,此事也就此做罷。
不一樣級別的領導,對於審批的額度是不同的,好比,項目經理只能審批500元之內的申請;部門經理能審批1000元之內的申請;而總經理能夠審覈任意額度的申請。
也就是說,當某人提出聚餐費用申請的請求後,該請求會經由項目經理、部門經理、總經理之中的某一位領導來進行相應的處理,可是提出申請的人並不知道最終會由誰來處理他的請求,通常申請人是把本身的申請提交給項目經理,或許最後是由總經理來處理他的請求。
可使用責任鏈模式來實現上述功能:當某人提出聚餐費用申請的請求後,該請求會在 項目經理—〉部門經理—〉總經理 這樣一條領導處理鏈上進行傳遞,發出請求的人並不知道誰會來處理他的請求,每一個領導會根據本身的職責範圍,來判斷是處理請求仍是把請求交給更高級別的領導,只要有領導處理了,傳遞就結束了。
須要把每位領導的處理獨立出來,實現成單獨的職責處理對象,而後爲它們提供一個公共的、抽象的父職責對象,這樣就能夠在客戶端來動態地組合職責鏈,實現不一樣的功能要求了。
抽象處理者角色類
public abstract class Handler { /** * 持有下一個處理請求的對象 */ protected Handler successor = null; /** * 取值方法 */ public Handler getSuccessor() { return successor; } /** * 設置下一個處理請求的對象 */ public void setSuccessor(Handler successor) { this.successor = successor; } /** * 處理聚餐費用的申請 * @param user 申請人 * @param fee 申請的錢數 * @return 成功或失敗的具體通知 */ public abstract String handleFeeRequest(String user , double fee); }
具體處理者角色
public class ProjectManager extends Handler { @Override public String handleFeeRequest(String user, double fee) { String str = ""; //項目經理權限比較小,只能在500之內 if(fee < 500) { //爲了測試,簡單點,只贊成張三的請求 if("張三".equals(user)) { str = "成功:項目經理贊成【" + user + "】的聚餐費用,金額爲" + fee + "元"; }else { //其餘人一概不一樣意 str = "失敗:項目經理不一樣意【" + user + "】的聚餐費用,金額爲" + fee + "元"; } }else { //超過500,繼續傳遞給級別更高的人處理 if(getSuccessor() != null) { return getSuccessor().handleFeeRequest(user, fee); } } return str; } }
public class DeptManager extends Handler { @Override public String handleFeeRequest(String user, double fee) { String str = ""; //部門經理的權限只能在1000之內 if(fee < 1000) { //爲了測試,簡單點,只贊成張三的請求 if("張三".equals(user)) { str = "成功:部門經理贊成【" + user + "】的聚餐費用,金額爲" + fee + "元"; }else { //其餘人一概不一樣意 str = "失敗:部門經理不一樣意【" + user + "】的聚餐費用,金額爲" + fee + "元"; } }else { //超過1000,繼續傳遞給級別更高的人處理 if(getSuccessor() != null) { return getSuccessor().handleFeeRequest(user, fee); } } return str; } }
public class GeneralManager extends Handler { @Override public String handleFeeRequest(String user, double fee) { String str = ""; //總經理的權限很大,只要請求到了這裏,他均可以處理 if(fee >= 1000) { //爲了測試,簡單點,只贊成張三的請求 if("張三".equals(user)) { str = "成功:總經理贊成【" + user + "】的聚餐費用,金額爲" + fee + "元"; }else { //其餘人一概不一樣意 str = "失敗:總經理不一樣意【" + user + "】的聚餐費用,金額爲" + fee + "元"; } }else { //若是還有後繼的處理對象,繼續傳遞 if(getSuccessor() != null) { return getSuccessor().handleFeeRequest(user, fee); } } return str; } }
客戶端類
public class Client { public static void main(String[] args) { //先要組裝責任鏈 Handler h1 = new GeneralManager(); Handler h2 = new DeptManager(); Handler h3 = new ProjectManager(); h3.setSuccessor(h2); h2.setSuccessor(h1); //開始測試 String test1 = h3.handleFeeRequest("張三", 300); System.out.println("test1 = " + test1); String test2 = h3.handleFeeRequest("李四", 300); System.out.println("test2 = " + test2); System.out.println("---------------------------------------"); String test3 = h3.handleFeeRequest("張三", 700); System.out.println("test3 = " + test3); String test4 = h3.handleFeeRequest("李四", 700); System.out.println("test4 = " + test4); System.out.println("---------------------------------------"); String test5 = h3.handleFeeRequest("張三", 1500); System.out.println("test5 = " + test5); String test6 = h3.handleFeeRequest("李四", 1500); System.out.println("test6 = " + test6); } }
運行結果以下所示:
一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行爲中選擇一個:一是承擔責任,而是把責任推給下家。不容許出現某一個具體處理者對象在承擔了一部分責任後又 把責任向下傳的狀況。
在一個純的責任鏈模式裏面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式裏面,一個請求能夠最終不被任何接收端對象所接收。
純的責任鏈模式的實際例子很難找到,通常看到的例子均是不純的責任鏈模式的實現。有些人認爲不純的責任鏈根本不是責任鏈模式,這也許是有道理的。可是在實際的系統裏,純的責任鏈很難找到。若是堅持責任鏈不純便不是責任鏈模式,那麼責任鏈模式便不會有太大意義了。
衆所周知Tomcat中的Filter就是使用了責任鏈模式,建立一個Filter除了要在web.xml文件中作相應配置外,還須要實現javax.servlet.Filter接口。
public class TestFilter implements Filter{ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } public void destroy() { } public void init(FilterConfig filterConfig) throws ServletException { } }
使用DEBUG模式所看到的結果以下
其實在真正執行到TestFilter類以前,會通過不少Tomcat內部的類。順帶提一下其實Tomcat的容器設置也是責任鏈模式,注意被紅色方框所圈中的類,從Engine到Host再到Context一直到Wrapper都是經過一個鏈傳遞請求。被綠色方框所圈中的地方有一個名爲ApplicationFilterChain的類,ApplicationFilterChain類所扮演的就是抽象處理者角色,而具體處理者角色由各個Filter扮演。
第一個疑問是ApplicationFilterChain將全部的Filter存放在哪裏?
答案是保存在ApplicationFilterChain類中的一個ApplicationFilterConfig對象的數組中。
/** * Filters. */ private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
那ApplicationFilterConfig對象又是什麼呢?
ApplicationFilterConfig是一個Filter容器。如下是ApplicationFilterConfig類的聲明:
/** * Implementation of a <code>javax.servlet.FilterConfig</code> useful in * managing the filter instances instantiated when a web application * is first started. * * @author Craig R. McClanahan * @version $Id: ApplicationFilterConfig.java 1201569 2011-11-14 01:36:07Z kkolinko $ */
當一個web應用首次啓動時ApplicationFilterConfig會自動實例化,它會從該web應用的web.xml文件中讀取配置的Filter的信息,而後裝進該容器。
剛剛看到在ApplicationFilterChain類中所建立的ApplicationFilterConfig數組長度爲零,那它是在何時被從新賦值的呢?
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
是在調用ApplicationFilterChain類的addFilter()方法時。
/** * The int which gives the current number of filters in the chain. */ private int n = 0;
public static final int INCREMENT = 10;
void addFilter(ApplicationFilterConfig filterConfig) { // Prevent the same filter being added multiple times for(ApplicationFilterConfig filter:filters) if(filter==filterConfig) return; if (n == filters.length) { ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT]; System.arraycopy(filters, 0, newFilters, 0, n); filters = newFilters; } filters[n++] = filterConfig; }
變量n用來記錄當前過濾器鏈裏面擁有的過濾器數目,默認狀況下n等於0,ApplicationFilterConfig對象數組的長度也等於0,因此當第一次調用addFilter()方法時,if (n == filters.length)的條件成立,ApplicationFilterConfig數組長度被改變。以後filters[n++] = filterConfig;將變量filterConfig放入ApplicationFilterConfig數組中並將當前過濾器鏈裏面擁有的過濾器數目+1。
那ApplicationFilterChain的addFilter()方法又是在什麼地方被調用的呢?
是在ApplicationFilterFactory類的createFilterChain()方法中。
1 public ApplicationFilterChain createFilterChain 2 (ServletRequest request, Wrapper wrapper, Servlet servlet) { 3 4 // get the dispatcher type 5 DispatcherType dispatcher = null; 6 if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) { 7 dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR); 8 } 9 String requestPath = null; 10 Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR); 11 12 if (attribute != null){ 13 requestPath = attribute.toString(); 14 } 15 16 // If there is no servlet to execute, return null 17 if (servlet == null) 18 return (null); 19 20 boolean comet = false; 21 22 // Create and initialize a filter chain object 23 ApplicationFilterChain filterChain = null; 24 if (request instanceof Request) { 25 Request req = (Request) request; 26 comet = req.isComet(); 27 if (Globals.IS_SECURITY_ENABLED) { 28 // Security: Do not recycle 29 filterChain = new ApplicationFilterChain(); 30 if (comet) { 31 req.setFilterChain(filterChain); 32 } 33 } else { 34 filterChain = (ApplicationFilterChain) req.getFilterChain(); 35 if (filterChain == null) { 36 filterChain = new ApplicationFilterChain(); 37 req.setFilterChain(filterChain); 38 } 39 } 40 } else { 41 // Request dispatcher in use 42 filterChain = new ApplicationFilterChain(); 43 } 44 45 filterChain.setServlet(servlet); 46 47 filterChain.setSupport 48 (((StandardWrapper)wrapper).getInstanceSupport()); 49 50 // Acquire the filter mappings for this Context 51 StandardContext context = (StandardContext) wrapper.getParent(); 52 FilterMap filterMaps[] = context.findFilterMaps(); 53 54 // If there are no filter mappings, we are done 55 if ((filterMaps == null) || (filterMaps.length == 0)) 56 return (filterChain); 57 58 // Acquire the information we will need to match filter mappings 59 String servletName = wrapper.getName(); 60 61 // Add the relevant path-mapped filters to this filter chain 62 for (int i = 0; i < filterMaps.length; i++) { 63 if (!matchDispatcher(filterMaps[i] ,dispatcher)) { 64 continue; 65 } 66 if (!matchFiltersURL(filterMaps[i], requestPath)) 67 continue; 68 ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) 69 context.findFilterConfig(filterMaps[i].getFilterName()); 70 if (filterConfig == null) { 71 // FIXME - log configuration problem 72 continue; 73 } 74 boolean isCometFilter = false; 75 if (comet) { 76 try { 77 isCometFilter = filterConfig.getFilter() instanceof CometFilter; 78 } catch (Exception e) { 79 // Note: The try catch is there because getFilter has a lot of 80 // declared exceptions. However, the filter is allocated much 81 // earlier 82 Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); 83 ExceptionUtils.handleThrowable(t); 84 } 85 if (isCometFilter) { 86 filterChain.addFilter(filterConfig); 87 } 88 } else { 89 filterChain.addFilter(filterConfig); 90 } 91 } 92 93 // Add filters that match on servlet name second 94 for (int i = 0; i < filterMaps.length; i++) { 95 if (!matchDispatcher(filterMaps[i] ,dispatcher)) { 96 continue; 97 } 98 if (!matchFiltersServlet(filterMaps[i], servletName)) 99 continue; 100 ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) 101 context.findFilterConfig(filterMaps[i].getFilterName()); 102 if (filterConfig == null) { 103 // FIXME - log configuration problem 104 continue; 105 } 106 boolean isCometFilter = false; 107 if (comet) { 108 try { 109 isCometFilter = filterConfig.getFilter() instanceof CometFilter; 110 } catch (Exception e) { 111 // Note: The try catch is there because getFilter has a lot of 112 // declared exceptions. However, the filter is allocated much 113 // earlier 114 } 115 if (isCometFilter) { 116 filterChain.addFilter(filterConfig); 117 } 118 } else { 119 filterChain.addFilter(filterConfig); 120 } 121 } 122 123 // Return the completed filter chain 124 return (filterChain); 125 126 }
能夠將如上代碼分爲兩段,51行以前爲第一段,51行以後爲第二段。
第一段的主要目的是建立ApplicationFilterChain對象以及一些參數設置。
第二段的主要目的是從上下文中獲取全部Filter信息,以後使用for循環遍歷並調用filterChain.addFilter(filterConfig);將filterConfig放入ApplicationFilterChain對象的ApplicationFilterConfig數組中。
那ApplicationFilterFactory類的createFilterChain()方法又是在什麼地方被調用的呢?
是在StandardWrapperValue類的invoke()方法中被調用的。
因爲invoke()方法較長,因此將不少地方省略。
public final void invoke(Request request, Response response) throws IOException, ServletException { ...省略中間代碼 // Create the filter chain for this request 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數組賦值。
根據上面的代碼能夠看出StandardWrapperValue類的invoke()方法在執行完createFilterChain()方法後,會繼續執行ApplicationFilterChain類的doFilter()方法,而後在doFilter()方法中會調用internalDoFilter()方法。
如下是internalDoFilter()方法的部分代碼
// Call the next filter if there is one if (pos < n) { //拿到下一個Filter,將指針向下移動一位 //pos它來標識當前ApplicationFilterChain(當前過濾器鏈)執行到哪一個過濾器 ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { //獲取當前指向的Filter的實例 filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response); 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()方法 filter.doFilter(request, response, this); }
這裏的filter.doFilter(request, response, this);就是調用咱們前面建立的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法會繼續調用chain.doFilter(request, response);方法,而這個chain其實就是ApplicationFilterChain,因此調用過程又回到了上面調用dofilter和調用internalDoFilter方法,這樣執行直到裏面的過濾器所有執行。
若是定義兩個過濾器,則Debug結果以下: