環境: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>