個人部落格原文位址:http://www.ltang.me/2016/01/22/filter-chain-study/
概述
有點類似springMVC中的filter,多個filter形成一條過濾鍊,依次處理一個請求,并傳給下一個filter,最後才分發到各個controller進行業務處理。
現在想實作的是這樣一種功能,使用一個過濾鍊處理一個請求:
- 時間過濾器在請求開始時記錄時間,結束後統計請求時間并列印;
- 日志過濾器在請求發送到伺服器前列印請求的資料,資料從伺服器傳回後列印傳回的資料;
- 其他…
思路
- 首先寫好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); }
- 實作一個過濾鍊類,implemts FilterChain:
- 首先定義一個靜态的Filter清單,在向這個清單添加filter之後,所有請求共用這個清單,被filters清單中的filter依次處理;
- 定義一個變量
,每個請求new一個FilterChain,以此儲存每個請求各自的一些參數,并将該FilterChain作為參數傳遞給Filter的doFilter方法,這樣,Filter處理不同的請求時,就能從attributes中拿到每個請求不同的參數;Map<String, Object>attributes = new HashMap<>()
- 實作doFilter方法: 注意這裡index不是靜态的,是以對于每個請求來說,它都是從第一個開始依次拿到filters清單中的Filter,并用這個Filter處理這個過濾鍊(通過this/fiterChain将attributes傳遞給每一個Filter),如果靜态的filter拿完了,就拿最後一個-每個請求自己定義的-Filter,這個過濾鍊就算完成了。
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; } }
- 實作共用的Filter,以計時過濾器TimeFilter為例: 當請求調用過濾鍊filterChain.doFilter()方法時,該請求線程從filters清單中拿到第一個Filter(即此時TimeFilter),記錄下目前時間,然後在try中使用該filterChain繼續拿下一個Filter并處理請求…一直傳遞下去,直到最後一個Filter(即請求自定義的Filter),在該Filter中,不再繼續向下傳遞,而是完成實際想要完成的操作。後面的操作結束後,終于輪到時間過濾器TimeFilter中的finally塊,記錄整個操作的時間。
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); } }
-
實作自定義的業務Filter和方法:
不同的請求有不同的業務需求,是以,先實作一個具體的業務方法,并将這個方法作為參數放入filterChain中的attributes中。每個過濾鍊中的最後一個Filter,就負責從filter中拿到這個方法,使用這個方法完成業務邏輯,至于方法需要的參數,已經儲存在attributes中了。
SendMessageFilter: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做的事情,就是從FilterChain執行個體的attributes中拿到實作的SendMessageAction的執行個體,并調用這個Action執行個體中的doAction完成業務邏輯。這個Action執行個體就是上面new并set進去的,在Action方法中,經過一系列操作,得到了結果response,并将結果放入attributes,這條過濾鍊處理完後,即調用完filterChain.doFilter()後,就可以通過filterChain.getAttribute(“response”)将action放進去的結果拿出來了。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); } ......