責任鏈模式在tomcat的應用
一、責任鏈模式責任鏈模式是一種物件的行為模式。在責任鏈模式裡,很多物件由每一個物件對其下家的引用而連線起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個物件決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個物件最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。 責任鏈模式涉及到的角色如下所示:
● 抽象處理者(Handler)角色:定義出一個處理請求的介面。如果需要,介面可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java介面實現。上圖中Handler類的聚合關係給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。
● 具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
原始碼
抽象處理者角色
- 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 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中的應用
過對Tomcat8.5.5版本的原始碼進行學習,發現Tomcat中有2個地方用到了責任鏈模式,一個是管道中的閥門,另一個是過濾器鏈中的過濾器。下面我們分別來看一下這兩個責任鏈模式具體是如何使用的。 Filter介面如下所示:
public interface Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain); }
Filter介面是所有具體過濾器必須實現的介面,該介面有一個核心的方法doFilter,方法對Request和Response進行處理,注意第三個引數型別為FilterChain,傳入該引數的目的是為了責任能夠傳遞到下一個過濾器。 具體Filter如下所示: public class MyFilter implements Filter{ @Override public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) { //處理request和response //... chain.doFilter(request,response); } }
首先根據需求對request和response進行處理,最後需要呼叫過濾器鏈中的doFilter方法以便責任能夠傳遞到下一個過濾器。chain.doFilter(request,response),注意這個doFilter是FilterChain中的doFilter與Filter中的doFilter沒有任何關係,名字雖然相同,但是引數是不一樣的。FilterChain中的doFilter只有Request和Response兩個引數,其實更好的命名方式應該是chain.doNextFilter(resquest,response)
那麼,FilterChain是什麼樣的呢?我們上面已經看到了FilterChain的用法,也知道了FilterChain在呼叫doFilter(request,response)的時候需要有能力將任務傳遞到下一個Filter,因此我們可以推斷,FilterChain中需要儲存所有的Filter。在Tomcat的實現中,正是在FilterChain中用一個數組儲存了所有的Filter,這不禁讓我想起了java.util.ArrayList的實現。 在Tomcat中FilterChain也是一個介面,如下所示:
public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response); }
而ApplicationFilterChain實現了FilterChain,其核心程式碼如下所示:
- public void doFilter(ServletRequest request, ServletResponse response)
- throws IOException, ServletException {
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- try {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction<Void>() {
- @Override
- public Void run()
- throws ServletException, IOException {
- internalDoFilter(req,res);
- return null;
- }
- }
- );
- } catch( PrivilegedActionException pe) {
- Exception e = pe.getException();
- if (e instanceof ServletException)
- throw (ServletException) e;
- else if (e instanceof IOException)
- throw (IOException) e;
- else if (e instanceof RuntimeException)
- throw (RuntimeException) e;
- else
- throw new ServletException(e.getMessage(), e);
- }
- } else {
- internalDoFilter(request,response);
- }
- }
- private void internalDoFilter(ServletRequest request,
- ServletResponse response)
- throws IOException, ServletException {
- // Call the next filter if there is one
- if (pos < n) {
- //在鏈執行過程中,當pos
- ApplicationFilterConfig filterConfig = filters[pos++];
- Filter filter = null;
- try {
- 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(request, response, this);
- }
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response);
- } catch (IOException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (ServletException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (RuntimeException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.filter"), e);
- }
- return;
- }
- // We fell off the end of the chain -- call the servlet instance
- try {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(request);
- lastServicedResponse.set(response);
- }
- support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
- servlet, request, response);
- if (request.isAsyncSupported()
- && !support.getWrapper().isAsyncSupported()) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
- }
- // Use potentially wrapped request from this point
- if ((request instanceof HttpServletRequest) &&
- (response instanceof HttpServletResponse)) {
- if( 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);
- }
- } else {
- servlet.service(request, response);
- }
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response);
- } catch (IOException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (ServletException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (RuntimeException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.servlet"), e);
- } finally {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(null);
- lastServicedResponse.set(null);
- }
- }
- }
可以看出在FilterChain的實現中確實是用陣列來儲存所有的過濾器,但是並不是直接儲存的Filter型別,而是儲存的FilterConfig型別,可以把FilterConfig當做Filter的包裝類,我們可以通過filterConfig.getFilter()拿到Filter例項。在internalDoFilter方法中我們看到了FilterChain是如何實現責任的傳遞的,通過pos和n這兩個變數來判斷有沒有傳遞到過濾器鏈的最後面,如果沒有到最後,則取出當前過濾器並呼叫filter.doFilter的方法,在前面MyFilter的實現當中,它的doFilter方法在最後又呼叫了chain.doFilter(),從而保證了過濾器鏈能夠將責任傳遞到下一個過濾器中。pos和n這兩個變數的作用又讓我聯想到了ArrayList中的迭代器的實現。 客戶端程式碼如下:
- public class Client{
- public static void main(String[] args) {
- Request request = new Request();
- Response response = new Response();
- Filter filter1 = new MyFilter1();
- Filter filter2 = new MyFilter2();
- Filter filter3 = new MyFilter3();
- FilterChain chain = new ApplicationFilterChain();
- ApplicationFilterConfig filterConfig1 = new ApplicationFilterConfig(filter1);
- ApplicationFilterConfig filterConfig2 = new ApplicationFilterConfig(filter2);
- ApplicationFilterConfig filterConfig3 = new ApplicationFilterConfig(filter3);
- chain.addFilter(filterConfig1);
- chain.addFilter(filterConfig2);
- chain.addFilter(filterConfig3);
- chain.doFilter(request, response);
- }
- }
以下是Filter介面doFilter定義如下 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
過濾器鏈裡面的filter在呼叫dofilter完成後,會繼續呼叫chain.doFilter(request,response)方法,而這個chain其實就是applicationfilterchain,所以呼叫過程又回到了上面呼叫dofilter和呼叫internalDoFilter方法,這樣執行直到裡面的過濾器全部執行
當filte都呼叫完成後,它就會初始化相應的servlet