环境:tomcat 8.0.28
Filter 原理
本文主要介绍
- filter 的配置方式与加载过程
- 每次请求,filter 是如何配合工作的
filter 配置与加载
配置
目前已知两种配置 Filter 的方法:
web.xml
中配置<filter>
@WebFilter
一般配置都会有两个部分:
filter 具体包括 filter class 、 filter name 及 一些属性
web.xml 配置中指的是
<filter>
@WebFilter
配置中包含被注解的类,和注解中的一些属性filter mapping 包括 filter name、URL Pattern
web.xml 配置中指的是
<filter-mapping>
@WebFilter
配置指的是value
或urlPatterns
这里注意
value
和URLPattern
虽然功能一样,但是不能同时配置,否则会报错
其实这两部分是分开存放的,因为配置的时候他俩总是同时出现,错觉让我们认为他俩是一个整体,并且 filter-mapping 是可以配置多个的,下面我们解释下这两个东西是如何实现的:
加载
filter
的具体信息被加载到 StandardContext.filterDefs 中,filter mapping
被存放到 StandardContext.filterMaps 中 ,初始化过程:
org.apache.catalina.startup.ContextConfig.configureContext(WebXml webxml)
// 设置 filter for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) { filter.setAsyncSupported("false"); } context.addFilterDef(filter); } // 设置 filter mapping for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); }
读取 @WebFilter 注解的 filter
在 org.apache.catalina.startup.ContextConfig.webConfig()
函数中每个步骤注释的很明确:
// Step 4. Process /WEB-INF/classes for annotations and @HandlesTypes matches
if (ok) {
WebResource[] webResources =
context.getResources().listResources("/WEB-INF/classes");
for (WebResource webResource : webResources) {
processAnnotationsWebResource(webResource, webXml,
webXml.isMetadataComplete());
}
}
在 processAnnotationsStream(InputStream is, WebXml fragment, boolean handlesTypesOnly)
中处理了三个注解:@WebServlet, @WebFilter, @WebListener
这里保留一个疑问
ApplicationFilterRegistration.addMappingForUrlPatterns
这里添加了一个filter (FilterMap[filterName=Tomcat WebSocket (JSR356) Filter, urlPattern=/*])不知道作用是什么
初始化 FilterChain
每次请求时,都要创建一个 FilterChain ,用来层层过滤、处理这次请求
一般见到 Chain 就知道这里运用了 责任链 模式,所以通过打断点,可以定位到 doFilter 的源头
filter.doFilter(request, response, this)
|
...
|
filterChain.doFilter(request, response)
filterChain.doFilter(request, response)
就是 责任链 开始的位置,顺藤摸瓜找到 filterChain 创建的位置:org.apache.catalina.core.StandardWrapperValve.invoke(Request request, Response response)
-> ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
查看 ApplicationFilterChain 源码可知 FilterChain 的具体初始化过程
Tips
在 SprintBoot 中使用
@WebServlet
、@WebFilter
、@WebListener
,需要在Application.class
上加注解 @ServletComponentScan ,这里初始化 filter 的过程在org.apache.catalina.core.ApplicationFilterConfig.initFilter()
,是跟上面有区别的详细见:
参考:
如果想让 filter 交给 spring 管理(即在 filter 中注入 bean),如下:
<filter> <filter-name>${filter.displayName}</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy </filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>${filter.beanName}</param-value> </init-param> </filter> <filter-mapping> <filter-name>${filter.displayName}</filter-name> <url-pattern>${filter.urlPattern}</url-pattern> </filter-mapping>