tomcat filter分析

Filter

  • Filterservlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用
  • FilterFilterChain密不可分, Filter可以实现依次调用正是因为有了FilterChain

img

Filter接口

1
2
3
4
5
6
7
8
9
10
11
12
13
// Filter.class
public interface Filter {
// 容器创建的时候调用, 即启动tomcat的时候调用
public void init(FilterConfig filterConfig) throws ServletException;
// 由FilterChain调用, 并且传入FilterChain本身, 最后回调FilterChain的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 容器销毁的时候调用, 即关闭tomcat的时候调用
public void destroy();
}

FilterChain接口

1
2
3
4
5
6
7
8
// FilterChain.class
public interface FilterChain {
// 由Filter.doFilter()中的chain.doFilter调用
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}

Tomcat filter源码

创建FilterChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// StandardWrapperValve.class
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// .....省略......
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC)
dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
// .....省略......
// 执行过滤器链doFilter()
filterChain.doFilter(request.getRequest(), response.getResponse());
// .....省略......
}

每次请求过来都会创建一个过滤器链(filterChain),并把待执行的servlet对象存放到过滤器链中。对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filterposition,以便继续向下调用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
26
27
28
29
30
31
// ApplicationFilterFactory.class
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// .....省略......
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// Add the relevant path-mapped filters to this filter chain
// 根据请求url找到匹配的filter,添加进来
for (int i = 0; i < filterMaps.length; i++) {
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
...
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
// 如果配置了根据servlet名称过滤,则再寻找一遍filter
for (int i = 0; i < filterMaps.length; i++) {
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
...
filterChain.addFilter(filterConfig);
}
}

创建顺序:

  1. 把要执行的servlet存放到过滤器链中。
  2. 如果没有配置过滤器则return一个空的过滤器链(只包含上面设置的servlet)。
  3. 如果配置url-pattern过滤器,则把匹配的过滤器加入到过滤器链中
  4. 如果配置servlet-name过滤器,则把匹配的过滤器加入到过滤器链中

注意: filterChain.addFilter()顺序与web.xml中定义的Filter顺序一致,所以过滤器的执行顺序是按定义的上下顺序决定的。

过滤器链执行

1
2
3
4
5
6
7
8
9
10
// StandardWrapperValve.class
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// .....省略......
if ((servlet != null) && (filterChain != null)) {
// 执行过滤器链doFilter()
filterChain.doFilter(request.getRequest(), response.getResponse());
// .....省略......
}
}

如果servlet和过滤器链都不为空,则开始调用过滤器链的doFilter()方法

1
2
3
4
5
6
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// .....省略......
// ***整个Filter流程的实现就是由这个方法完成***
internalDoFilter(request,response);
}
ApplicationFilterChain.internalDoFilter()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
// 获取Filter
Filter filter = filterConfig.getFilter();
// .....省略......
// 调用Filter的doFilter方法并把FilterChain本身传进去(this)
filter.doFilter(request, response, this);
return;
}
// We fell off the end of the chain -- call the servlet instance
// .....省略......
// 过滤器全部执行后,执行Servlet的service方法(自己的业务逻辑)
servlet.service(request, response);
}

FilterChain调用Filter后,Filter再执行FilterChain.doFilter(request, response),可见FilterChainFilter构成回调模式(这个也是与Spring Interceptor不同点, Interceptor基于java反射机制)

案例

假设下面的Filter就是调用链中的最后一个Filter

1
2
3
4
5
6
7
8
9
public class LogFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
Log.info("before");
chain.doFilter(request, response);
Log.info("after");
}
}
  1. LogFilter调用chain.doFilter之后就跳过了if语句从而调用了真正的servlet, 然后internalDoFilter方法就结束(出栈)了, 紧接着就是调用Log.info("after")了, 然后LogFilter的doFilter就结束了(也出栈了), 紧接着就是internalDoFilterfilter.doFilter(request, response, this)的结束然后return, 然后就是调用上一个filterchain.doFilter()之后的代码, 以此类推.
  2. Filter调用链的实现其实就是一个方法调用链的过程. 刚开始, Filter Chain每调用一个Filter.doFilter()方法就是向方法调用栈中进行压栈操作(代码上的体现就是执行Filter.doFilter之前的代码), 当Filter全部调用完成之后就调用真正处理请求的servlet, 然后由方法调用链自动进行出栈操作(代码上的体现就是执行Filter.doFilter之后的代码), 从而完成整个Filter的调用链. 因为Filter功能实现实际上就是利用了方法的压栈出栈, 所以可以在调用chain.doFilter之前将方法返回, 让容器不在调用servlet方法, 从而实现权限的控制, 关键词的过滤等功能.

参考

Tomcat Filter 源码分析

Filter 和 Interceptor 的比较

热评文章