【Tomcat】Filter 原理

環境: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 配置指的是 valueurlPatterns

    這裏注意 valueURLPattern 雖然功能一樣,但是不能同時配置,否則會報錯

其實這兩部分是分開存放的,因爲配置的時候他倆總是同時出現,錯覺讓我們認爲他倆是一個整體,並且 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(),是跟上面有區別的

    詳細見:

    參考:

    Spring Boot 過濾器、監聽器

  • 如果想讓 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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章