1. 程式人生 > >Spring-web原始碼解析之Filter-OncePerRequestFilter

Spring-web原始碼解析之Filter-OncePerRequestFilter

轉自:  http://blog.csdn.net/ktlifeng/article/details/50630934


基於4.1.7.RELEASE


我們先看一個filter-mapping的配置 

[html]  view plain  copy
  1. <filter-mapping>  
  2.    <filter-name
    >encodingFilter</filter-name>  
  3.    <url-pattern>/*</url-pattern>  
  4.    <dispatcher>REQUEST</dispatcher>  
  5.    <dispatcher>ASYNC</dispatcher>  
  6. </filter-mapping
    >  

這裡指定了一個ASYNC的配置,表明過濾非同步請求,這個ASYNC即是列舉類DispatcherType中的一個元素,在Servlet3.0中,如果一個請求是DispatcherType.ASYNC型別的,那麼在一個單一請求的過程中,filter能夠被多個執行緒呼叫,也就是意味著一個filter可能在一次請求中被多次執行,這顯然是會有問題的,那麼spring是怎麼避免這個問題的呢?這就是今天要說的OncePerRequestFilter。

直接看doFilter方法

[java]
  view plain  copy
  1. @Override  
  2. public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  3.       throws ServletException, IOException {  
  4.   
  5.    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  6.       throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  7.    }  
  8.    HttpServletRequest httpRequest = (HttpServletRequest) request;  
  9.    HttpServletResponse httpResponse = (HttpServletResponse) response;  
  10.   
  11.    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  12.    boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;  
  13.   
  14.    if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {  
  15.   
  16.       // Proceed without invoking this filter...  
  17.       filterChain.doFilter(request, response);  
  18.    }  
  19.    else {  
  20.       // Do invoke this filter...  
  21.       request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  22.       try {  
  23.          doFilterInternal(httpRequest, httpResponse, filterChain);  
  24.       }  
  25.       finally {  
  26.          // Remove the "already filtered" request attribute for this request.  
  27.          request.removeAttribute(alreadyFilteredAttributeName);  
  28.       }  
  29.    }  
  30. }  

從原始碼可以看出,spring會給已經過濾過的request設定一個attribute,在filter鏈和目標方法執行完畢之後才會釋放這個attribute,attribute的名字是從  getAlreadyFilteredAttributeName() 方法得來,預設為filter的名字加字尾,如果filter沒有完全初始化,則改為類名加字尾,字尾為“.FILTERED”

[java]  view plain  copy
  1. protected String getAlreadyFilteredAttributeName() {  
  2.    String name = getFilterName();  
  3.    if (name == null) {  
  4.       name = getClass().getName();  
  5.    }  
  6.    return name + ALREADY_FILTERED_SUFFIX;  
  7. }  

獲取完名字之後,需要進行判斷是否已經執行過這個filter了,判斷條件有3個

1 是否有hasAlreadyFilteredAttribute 

2 是否skipDispatch

3 是否不進行過濾

我們直接看2和3,步驟3裡,根據shouldNotFilter(httpRequest)來判斷是否進行過濾,其實現依賴於子類。

在2裡面判斷條件有兩種

[java]  view plain  copy
  1. private boolean skipDispatch(HttpServletRequest request) {  
  2.    if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {  
  3.       return true;  
  4.    }  
  5.    if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {  
  6.       return true;  
  7.    }  
  8.    return false;  
  9. }  

1 是非同步並且不應該過濾非同步,則skipDispatch為true,即不進行過濾

2 是ERROR請求並且不應該過濾ERROR,同樣返回true

在上述所有判斷條件完成之後,就可以決定是否執行

[java]  view plain  copy
  1. doFilterInternal(httpRequest, httpResponse, filterChain);  

方法了,這個方法就是子類具體實現的方法,其中之一便是前一篇文章中講的CharacterEncodingFilter。

還需要注意的一個方法是 

[java]  view plain  copy
  1. protected boolean isAsyncStarted(HttpServletRequest request) {  
  2.    return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();  
  3. }  

如果這個返回true,那麼當前執行緒結束時不會將response提交回去。

1

轉自:  http://blog.csdn.net/ktlifeng/article/details/50630934


基於4.1.7.RELEASE


我們先看一個filter-mapping的配置 

[html]  view plain  copy
  1. <filter-mapping>  
  2.    <filter-name>encodingFilter</filter-name>  
  3.    <url-pattern>/*</url-pattern>  
  4.    <dispatcher>REQUEST</dispatcher>  
  5.    <dispatcher>ASYNC</dispatcher>  
  6. </filter-mapping>  

這裡指定了一個ASYNC的配置,表明過濾非同步請求,這個ASYNC即是列舉類DispatcherType中的一個元素,在Servlet3.0中,如果一個請求是DispatcherType.ASYNC型別的,那麼在一個單一請求的過程中,filter能夠被多個執行緒呼叫,也就是意味著一個filter可能在一次請求中被多次執行,這顯然是會有問題的,那麼spring是怎麼避免這個問題的呢?這就是今天要說的OncePerRequestFilter。

直接看doFilter方法

[java]  view plain  copy
  1. @Override  
  2. public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  3.       throws ServletException, IOException {  
  4.   
  5.    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  6.       throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  7.    }  
  8.    HttpServletRequest httpRequest = (HttpServletRequest) request;  
  9.    HttpServletResponse httpResponse = (HttpServletResponse) response;  
  10.   
  11.    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  12.    boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;  
  13.   
  14.    if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {  
  15.   
  16.       // Proceed without invoking this filter...  
  17.       filterChain.doFilter(request, response);  
  18.    }  
  19.    else {  
  20.       // Do invoke this filter...  
  21.       request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  22.       try {  
  23.          doFilterInternal(httpRequest, httpResponse, filterChain);  
  24.       }  
  25.       finally {  
  26.          // Remove the "already filtered" request attribute for this request.  
  27.          request.removeAttribute(alreadyFilteredAttributeName);  
  28.       }  
  29.    }  
  30. }  

從原始碼可以看出,spring會給已經過濾過的request設定一個attribute,在filter鏈和目標方法執行完畢之後才會釋放這個attribute,attribute的名字是從  getAlreadyFilteredAttributeName() 方法得來,預設為filter的名字加字尾,如果filter沒有完全初始化,則改為類名加字尾,字尾為“.FILTERED”

[java]  view plain  copy
  1. protected String getAlreadyFilteredAttributeName() {  
  2.    String name = getFilterName();  
  3.    if (name == null) {  
  4.       name = getClass().getName();  
  5.    }  
  6.    return name + ALREADY_FILTERED_SUFFIX;  
  7. }  

獲取完名字之後,需要進行判斷是否已經執行過這個filter了,判斷條件有3個

1 是否有hasAlreadyFilteredAttribute 

2 是否skipDispatch

3 是否不進行過濾

我們直接看2和3,步驟3裡,根據shouldNotFilter(httpRequest)來判斷是否進行過濾,其實現依賴於子類。

在2裡面判斷條件有兩種

[java]  view plain  copy
  1. private boolean skipDispatch(HttpServletRequest request) {  
  2.    if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {  
  3.       return true;  
  4.    }  
  5.    if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {  
  6.       return true;  
  7. 相關推薦

    Spring-web原始碼解析Filter-OncePerRequestFilter

    轉自:  http://blog.csdn.net/ktlifeng/article/details/50630934 基於4.1.7.RELEASE 我們先看一個filter-mapping的配置 

    Spring IoC原始碼解析invokeBeanFactoryPostProcessors

    一、Bean工廠的後置處理器   Bean工廠的後置處理器:BeanFactoryPostProcessor(觸發時機:bean定義註冊之後bean例項化之前)和BeanDefinitionRegistryPostProcessor(觸發時機:bean定義註冊之前),所以可以在Bean工廠的後置處理器中修改B

    Spring IoC原始碼解析getBean

    一、例項化所有的非懶載入的單例項Bean   從org.springframework.context.support.AbstractApplicationContext#refresh方法開發,進入到例項化所有的非懶載入的單例項Bean的finishBeanFactoryInitialization(be

    spring原始碼解析AOP原理

    一、準備工作   在這裡我先簡單記錄下如何實現一個aop: AOP:【動態代理】 指在程式執行期間動態的將某段程式碼切入到指定方法指定位置進行執行的程式設計方式; 1、匯入aop模組;Spring AOP:(spring-aspects) 2、定義一個業務邏輯類(

    Spring原始碼解析 Spring Security啟動細節和工作模式

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    Spring原始碼解析四(容器的功能擴充套件)

    容器的功能擴充套件 1、概述 之前的文章中BeanFactory介面以及它的預設實現類XmlBeanFactory為例進行分析,但是Spring中還提供了另一個介面ApplicationContext,用於擴充套件BeanFactory中現有的功能。  ApplicationCon

    Spring原始碼解析四(bean載入)

    1、概述 已經分析了spring對於xml配置檔案的解析,接下來就是對bean的載入。 getBean的實現 public <T> T getBean(String name, Class<T> requiredType, Object... args) thro

    Spring原始碼解析三(自定義標籤的解析

    自定義標籤的解析 1、概述:通過前面的文章我們分析了預設標籤的解析,我們先回顧下自定義標籤解析所使用的方法 /** * Parse the elements at the root level in the document: * "import", "alias", "bean".

    Spring原始碼解析二(預設標籤的解析

    預設標籤解析 概述:本節重點詳細分析預設標籤的解析過程。接上一篇文章講到parseBeanDefinitions(root, delegate); /** * Parse the elements at the root level in the document: * "impor

    Spring原始碼解析SpringMVC

    1、說在前面的話 ①、在說springmvc之前先說一下與之相關的一些類與介面:ContextLoaderListener與ServletContextListener,ContextLoaderListener實現了ServletContextListener介面。 Context

    Spring原始碼解析標籤的解析下篇

    正文 上篇文章我們介紹了Spring預設標籤的解析,本文我們來分析一下Spring自定義標籤的解析。上篇文章我們瞭解到Spring的預設標籤目前有4個(import、alias、bean、beans),也就是說除了這4個標籤以外的標籤都是自定義標籤(當然這裡所說的標籤不包括

    Spring原始碼解析ApplicationContext

    閱讀須知 Spring原始碼版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 ApplicationContext applicationContext = new ClassPathXmlApplicationContext

    spring beans原始碼解讀--bean definiton解析

    spring提供了有兩種方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即屬性檔案格式的bean definition解析器和xml檔案格式的bean definition解析器。 我們先從簡單的Prop

    Spring原始碼解析bean的建立

    閱讀須知 Spring原始碼版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 之前我們都是在圍繞 ApplicationContext applicationContext = new ClassPathXmlApplicati

    spring原始碼解析IOC容器(一)

      學習優秀框架的原始碼,是提升個人技術水平必不可少的一個環節。如果只是停留在知道怎麼用,但是不懂其中的來龍去脈,在技術的道路上註定走不長遠。最近,學習了一段時間的spring原始碼,現在整理出來,以便日後溫故知新。   IOC容器是spring最核心的模組之一,是整個spring體系的基石,spring其

    spring原始碼解析IOC容器(二)------載入和註冊

      上一篇跟蹤了IOC容器對配置檔案的定位,現在我們繼續跟蹤程式碼,看看IOC容器是怎麼載入和註冊配置檔案中的資訊的。開始之前,首先我們先來了解一下IOC容器所使用的資料結構-------BeanDefinition,它是一個上層介面,有很多實現類,分別對應不同的資料載體。我們平時開發的時候,也會定義很多po

    spring原始碼解析IOC容器(三)——依賴注入

      上一篇主要是跟蹤了IOC容器對bean標籤進行解析之後存入Map中的過程,這些bean只是以BeanDefinition為載體單純的儲存起來了,並沒有轉換成一個個的物件,今天繼續進行跟蹤,看一看IOC容器是怎樣例項化物件的。   我們都使用過以下程式碼: 1 FileSystemXmlApplicati

    Spring原始碼解析ConfigurableApplicationContext

    UML圖 介面的作用 從上面的UML圖中,可以看到 ConfigurableApplicationContext 直接繼承了 ApplicationContext, Lifecycle, Closeable 介面,所以 ApplicationContext 是 ApplicationContext 的子類

    Spring原始碼解析@Configuration

    @Configuration簡介 用於標識一個類為配置類,與xml配置效果類似 用法簡介 public class TestApplication { public static void main(String args[]) { AnnotationConfigApplicati

    Spring Cloud系列(三):Eureka原始碼解析服務端

    一、自動裝配   1、根據自動裝配原理(詳見:Spring Boot系列(二):Spring Boot自動裝配原理解析),找到spring-cloud-starter-netflix-eureka-server.jar的spring.factories,檢視spring.factories如下:   2、進