天天看點

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

《JAVA與模式》之責任鍊模式

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責任鍊(Chain of Responsibility)模式的:

  責任鍊模式是一種對象的行為模式。在責任鍊模式裡,很多對象由每一個對象對其下家的引用而連接配接起來形成一條鍊。請求在這個鍊上傳遞,直到鍊上的某一個對象決定處理此請求。發出這個請求的用戶端并不知道鍊上的哪一個對象最終處理這個請求,這使得系統可以在不影響用戶端的情況下動态地重新組織和配置設定責任。

從擊鼓傳花談起

  擊鼓傳花是一種熱鬧而又緊張的飲酒遊戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。

  比如說,賈母、賈赦、賈政、賈寶玉和賈環是五個參加擊鼓傳花遊戲的傳花者,他們組成一個環鍊。擊鼓者将花傳給賈母,開始傳花遊戲。花由賈母傳給賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環,由賈環傳回給賈母,如此往複,如下圖所示。當鼓聲停止時,手中有花的人就得執行酒令。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  擊鼓傳花便是責任鍊模式的應用。責任鍊可能是一條直線、一個環鍊或者一個樹結構的一部分。

責任鍊模式的結構

  下面使用了一個責任鍊模式的最簡單的實作。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  責任鍊模式涉及到的角色如下所示:

  ●  抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設定和傳回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實作。上圖中Handler類的聚合關系給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。

  ●  具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇将請求處理掉,或者将請求傳給下家。由于具體處理者持有對下家的引用,是以,如果需要,具體處理者可以通路下家。

源代碼

  抽象處理者角色

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
public abstract class Handler {
    
    /**
     * 持有後繼的責任對象
     */
    protected Handler successor;
    /**
     * 示意處理請求的方法,雖然這個示意方法是沒有傳入參數的
     * 但實際是可以傳入參數的,根據具體需要來選擇是否傳遞參數
     */
    public abstract void handleRequest();
    /**
     * 取值方法
     */
    public Handler getSuccessor() {
        return successor;
    }
    /**
     * 指派方法,設定後繼的責任對象
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    
}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  具體處理者角色

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
public class ConcreteHandler extends Handler {
    /**
     * 處理方法,調用此方法處理請求
     */
    @Override
    public void handleRequest() {
        /**
         * 判斷是否有後繼的責任對象
         * 如果有,就轉發請求給後繼的責任對象
         * 如果沒有,則處理請求
         */
        if(getSuccessor() != null)
        {            
            System.out.println("放過請求");
            getSuccessor().handleRequest();            
        }else
        {            
            System.out.println("處理請求");
        }
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  用戶端類

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
public class Client {

    public static void main(String[] args) {
        //組裝責任鍊
        Handler handler1 = new ConcreteHandler();
        Handler handler2 = new ConcreteHandler();
        handler1.setSuccessor(handler2);
        //送出請求
        handler1.handleRequest();
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  可以看出,用戶端建立了兩個處理者對象,并指定第一個處理者對象的下家是第二個處理者對象,而第二個處理者對象沒有下家。然後用戶端将請求傳遞給第一個處理者對象。

  由于本示例的傳遞邏輯非常簡單:隻要有下家,就傳給下家處理;如果沒有下家,就自行處理。是以,第一個處理者對象接到請求後,會将請求傳遞給第二個處理者對象。由于第二個處理者對象沒有下家,于是自行處理請求。活動時序圖如下所示。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

使用場景

  來考慮這樣一個功能:申請聚餐費用的管理。

  很多公司都是這樣的福利,就是項目組或者是部門可以向公司申請一些聚餐費用,用于組織項目組成員或者是部門成員進行聚餐活動。

  申請聚餐費用的大緻流程一般是:由申請人先填寫申請單,然後交給上司審批,如果申請準許下來,上司會通知申請人審批通過,然後申請人去财務領取費用,如果沒有準許下來,上司會通知申請人審批未通過,此事也就此作罷。

  不同級别的上司,對于審批的額度是不一樣的,比如,項目經理隻能審批500元以内的申請;部門經理能審批1000元以内的申請;而總經理可以稽核任意額度的申請。

  也就是說,當某人提出聚餐費用申請的請求後,該請求會經由項目經理、部門經理、總經理之中的某一位上司來進行相應的處理,但是提出申請的人并不知道最終會由誰來處理他的請求,一般申請人是把自己的申請送出給項目經理,或許最後是由總經理來處理他的請求。

  

  可以使用責任鍊模式來實作上述功能:當某人提出聚餐費用申請的請求後,該請求會在 項目經理—〉部門經理—〉總經理 這樣一條上司處理鍊上進行傳遞,送出請求的人并不知道誰會來處理他的請求,每個上司會根據自己的職責範圍,來判斷是處理請求還是把請求交給更進階别的上司,隻要有上司處理了,傳遞就結束了。

  需要把每位上司的處理獨立出來,實作成單獨的職責處理對象,然後為它們提供一個公共的、抽象的父職責對象,這樣就可以在用戶端來動态地組合職責鍊,實作不同的功能要求了。

  

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

源代碼

  抽象處理者角色類

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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);
}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  具體處理者角色

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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;
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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;
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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;
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  用戶端類

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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);
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

運作結果如下所示:

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

純的與不純的責任鍊模式

  一個純的責任鍊模式要求一個具體的處理者對象隻能在兩個行為中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任後又 把責任向下傳的情況。

  在一個純的責任鍊模式裡面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鍊模式裡面,一個請求可以最終不被任何接收端對象所接收。

  純的責任鍊模式的實際例子很難找到,一般看到的例子均是不純的責任鍊模式的實作。有些人認為不純的責任鍊根本不是責任鍊模式,這也許是有道理的。但是在實際的系統裡,純的責任鍊很難找到。如果堅持責任鍊不純便不是責任鍊模式,那麼責任鍊模式便不會有太大意義了。

責任鍊模式在Tomcat中的應用

  衆所周知Tomcat中的Filter就是使用了責任鍊模式,建立一個Filter除了要在web.xml檔案中做相應配置外,還需要實作javax.servlet.Filter接口。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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 {
    }

}      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  使用DEBUG模式所看到的結果如下

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  其實在真正執行到TestFilter類之前,會經過很多Tomcat内部的類。順帶提一下其實Tomcat的容器設定也是責任鍊模式,注意被紅色方框所圈中的類,從Engine到Host再到Context一直到Wrapper都是通過一個鍊傳遞請求。被綠色方框所圈中的地方有一個名為ApplicationFilterChain的類,ApplicationFilterChain類所扮演的就是抽象處理者角色,而具體處理者角色由各個Filter扮演。

  第一個疑問是ApplicationFilterChain将所有的Filter存放在哪裡?

  答案是儲存在ApplicationFilterChain類中的一個ApplicationFilterConfig對象的數組中。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
/**
     * Filters.
     */
    private ApplicationFilterConfig[] filters = 
        new ApplicationFilterConfig[0];      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  那ApplicationFilterConfig對象又是什麼呢?

    ApplicationFilterConfig是一個Filter容器。以下是ApplicationFilterConfig類的聲明:

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
/**
 * 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 $
 */      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  當一個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;      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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;

    }      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  變量n用來記錄目前過濾器鍊裡面擁有的過濾器數目,預設情況下n等于0,ApplicationFilterConfig對象數組的長度也等于0,是以當第一次調用addFilter()方法時,if (n == filters.length)的條件成立,ApplicationFilterConfig數組長度被改變。之後filters[n++] = filterConfig;将變量filterConfig放入ApplicationFilterConfig數組中并将目前過濾器鍊裡面擁有的過濾器數目+1。

  那ApplicationFilterChain的addFilter()方法又是在什麼地方被調用的呢?

  是在ApplicationFilterFactory類的createFilterChain()方法中。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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     }      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  可以将如上代碼分為兩段,51行之前為第一段,51行之後為第二段。

  第一段的主要目的是建立ApplicationFilterChain對象以及一些參數設定。

  第二段的主要目的是從上下文中擷取所有Filter資訊,之後使用for循環周遊并調用filterChain.addFilter(filterConfig);将filterConfig放入ApplicationFilterChain對象的ApplicationFilterConfig數組中。

  那ApplicationFilterFactory類的createFilterChain()方法又是在什麼地方被調用的呢?

  是在StandardWrapperValue類的invoke()方法中被調用的。

  

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  由于invoke()方法較長,是以将很多地方省略。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
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());
  ...省略中間代碼
    }      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  那正常的流程應該是這樣的:

  在StandardWrapperValue類的invoke()方法中調用ApplicationFilterChai類的createFilterChain()方法———>在ApplicationFilterChai類的createFilterChain()方法中調用ApplicationFilterChain類的addFilter()方法———>在ApplicationFilterChain類的addFilter()方法中給ApplicationFilterConfig數組指派。

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  根據上面的代碼可以看出StandardWrapperValue類的invoke()方法在執行完createFilterChain()方法後,會繼續執行ApplicationFilterChain類的doFilter()方法,然後在doFilter()方法中會調用internalDoFilter()方法。

  以下是internalDoFilter()方法的部分代碼

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
// 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);
                }      
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用

  這裡的filter.doFilter(request, response, this);就是調用我們前面建立的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法會繼續調用chain.doFilter(request, response);方法,而這個chain其實就是ApplicationFilterChain,是以調用過程又回到了上面調用dofilter和調用internalDoFilter方法,這樣執行直到裡面的過濾器全部執行。

  如果定義兩個過濾器,則Debug結果如下:

【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用
【責任鍊模式】Java版簡單執行個體與Tomcat執行個體 從擊鼓傳花談起 責任鍊模式的結構 使用場景 純的與不純的責任鍊模式 責任鍊模式在Tomcat中的應用