天天看點

簡單過濾鍊的實作思路

個人部落格原文位址:http://www.ltang.me/2016/01/22/filter-chain-study/

概述

有點類似springMVC中的filter,多個filter形成一條過濾鍊,依次處理一個請求,并傳給下一個filter,最後才分發到各個controller進行業務處理。

現在想實作的是這樣一種功能,使用一個過濾鍊處理一個請求:

  1. 時間過濾器在請求開始時記錄時間,結束後統計請求時間并列印;
  2. 日志過濾器在請求發送到伺服器前列印請求的資料,資料從伺服器傳回後列印傳回的資料;
  3. 其他…

思路

  1. 首先寫好fiter和filterChain的接口類:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
          
    public interface Filter {
        void init();
        void doFilter(FilterChain chain) throws TException;
        void destory();
    }
    
    public interface FilterChain {
        void doFilter() throws TException;
        Object getAttribute(String name);
        void setAttribute(String name, Object value);
    }
          
  2. 實作一個過濾鍊類,implemts FilterChain:
    1. 首先定義一個靜态的Filter清單,在向這個清單添加filter之後,所有請求共用這個清單,被filters清單中的filter依次處理;
    2. 定義一個變量

      Map<String, Object>attributes = new HashMap<>()

      ,每個請求new一個FilterChain,以此儲存每個請求各自的一些參數,并将該FilterChain作為參數傳遞給Filter的doFilter方法,這樣,Filter處理不同的請求時,就能從attributes中拿到每個請求不同的參數;
    3. 實作doFilter方法:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
            
      protected int index = -1;
         protected Filter lastFilter;
      	
         @Override
         public void doFilter() throws TException {
             next().doFilter(this);
         }
      	
         public Filter next() {
             Filter filter = nextFilter();
             return filter == null ? lastFilter : filter;
         }
      	
         protected Filter nextFilter() {
             if (++index >= filters.size())
                 return null;
             else {
                 Filter filter = null;
                 try {
                     filter = filters.get(index);
                 } catch (IndexOutOfBoundsException e) {
                 }
                 return filter;
             }
         }
            
      注意這裡index不是靜态的,是以對于每個請求來說,它都是從第一個開始依次拿到filters清單中的Filter,并用這個Filter處理這個過濾鍊(通過this/fiterChain将attributes傳遞給每一個Filter),如果靜态的filter拿完了,就拿最後一個-每個請求自己定義的-Filter,這個過濾鍊就算完成了。
  3. 實作共用的Filter,以計時過濾器TimeFilter為例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
          
    @Override
    public void doFilter(FilterChain chain) throws TException {
        final long startTime = System.currentTimeMillis();
       
        try {
            chain.doFilter();
        } finally {
            LOGGER.info("耗時:{}ms",  System.currentTimeMillis() - startTime);
        }
    }
          
    當請求調用過濾鍊filterChain.doFilter()方法時,該請求線程從filters清單中拿到第一個Filter(即此時TimeFilter),記錄下目前時間,然後在try中使用該filterChain繼續拿下一個Filter并處理請求…一直傳遞下去,直到最後一個Filter(即請求自定義的Filter),在該Filter中,不再繼續向下傳遞,而是完成實際想要完成的操作。後面的操作結束後,終于輪到時間過濾器TimeFilter中的finally塊,記錄整個操作的時間。
  4. 實作自定義的業務Filter和方法:

    不同的請求有不同的業務需求,是以,先實作一個具體的業務方法,并将這個方法作為參數放入filterChain中的attributes中。每個過濾鍊中的最後一個Filter,就負責從filter中拿到這個方法,使用這個方法完成業務邏輯,至于方法需要的參數,已經儲存在attributes中了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
          
    final StubFilterChain stubFilterChain = new StubFilterChain();
    stubFilterChain.setLastFilter(new SendMessageFilter());
    
    stubFilterChain.setAttribute("request", request);
    stubFilterChain.setAttribute("sendMessage", (SendMessageFilter.SendMessageAction)(chain) -> {
        
         RESP resp = {...};
         chain.setAttribute("response", resp);
     });
    
    stubFilterChain.doFilter();
    return (RESP) stubFilterChain.getAttribute("response");
          
    SendMessageFilter:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
          
    public class SendMessageFilter implements Filter {
    
       public interface SendMessageAction {
           void doAction(FilterChain chain) throws TException;
       }
    
       @Override
       public void doFilter(FilterChain chain) throws TException {
           SendMessageAction action = (SendMessageAction) chain.getAttribute("sendMessage");
           action.doAction(chain);
       }
       ......
          
    可以看到,SendMessageFilter做的事情,就是從FilterChain執行個體的attributes中拿到實作的SendMessageAction的執行個體,并調用這個Action執行個體中的doAction完成業務邏輯。這個Action執行個體就是上面new并set進去的,在Action方法中,經過一系列操作,得到了結果response,并将結果放入attributes,這條過濾鍊處理完後,即調用完filterChain.doFilter()後,就可以通過filterChain.getAttribute(“response”)将action放進去的結果拿出來了。