个人博客原文地址: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依次处理;
- 定义一个变量
Map<String, Object>attributes = new HashMap<>()
,每个请求new一个FilterChain,以此保存每个请求各自的一些参数,并将该FilterChain作为参数传递给Filter的doFilter方法,这样,Filter处理不同的请求时,就能从attributes中拿到每个请求不同的参数; -
实现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,这个过滤链就算完成了。
-
实现共用的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块,记录整个操作的时间。
-
实现自定义的业务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放进去的结果拿出来了。