Spring web過濾器-各種filter講解




http://liuluo129.iteye.com/blog/1965268

圖來自上面連接


http://blog.csdn.net/geloin/article/details/7441330

整理於上面


Spring的web包中中有很多過濾器,這些過濾器位於org.springframework.web.filter並且理所當然地實現了javax.servlet.Filter,不過實現的方式有以下幾類:

        (1) 直接實現Filter,這一類過濾器只有CompositeFilter;

        (2) 繼承抽象類GenericFilterBean,該類實現了javax.servlet.Filter,這一類的過濾器只有一個,即DelegatingFilterProxy;

        (3) 繼承抽象類OncePerRequestFilter,該類爲GenericFilterBean的直接子類,這一類過濾器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;

        (4) 繼承抽象類AbstractRequestLoggingFilter,該類爲OncePerRequestFilter的直接子類,這一類過濾器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。

        本文要講述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。

        GenericFilterBean

        抽象類GenericFilterBean實現了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五個接口,作用如下:

        (1) Filter,實現過濾器;

        (2) BeanNameAware,實現該接口的setBeanName方法,便於Bean管理器生成Bean;

        (3) EnvironmentAware,實現該接口的setEnvironment方法,指明該Bean運行的環境;

        (4) ServletContextAware,實現該接口的setServletContextAware方法,指明上下文;

        (5) InitializingBean,實現該接口的afterPropertiesSet方法,指明設置屬性生的操作;

        (6) DisposableBean,實現該接口的destroy方法,用於回收資源。

        GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在該類中實現,doFilter在具體實現類中實現。init的代碼如下:

[java] view plain copy
  1. /** 
  2.      * Standard way of initializing this filter. 
  3.      * Map config parameters onto bean properties of this filter, and 
  4.      * invoke subclass initialization. 
  5.      * @param filterConfig the configuration for this filter 
  6.      * @throws ServletException if bean properties are invalid (or required 
  7.      * properties are missing), or if subclass initialization fails. 
  8.      * @see #initFilterBean 
  9.      */  
  10.     public final void init(FilterConfig filterConfig) throws ServletException {  
  11.         Assert.notNull(filterConfig, "FilterConfig must not be null");  
  12.         if (logger.isDebugEnabled()) {  
  13.             logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");  
  14.         }  
  15.   
  16.         this.filterConfig = filterConfig;  
  17.   
  18.         // Set bean properties from init parameters.  
  19.         try {  
  20.             PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);  
  21.             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  22.             ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());  
  23.             bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, this.environment));  
  24.             initBeanWrapper(bw);  
  25.             bw.setPropertyValues(pvs, true);  
  26.         }  
  27.         catch (BeansException ex) {  
  28.             String msg = "Failed to set bean properties on filter '" +  
  29.                 filterConfig.getFilterName() + "': " + ex.getMessage();  
  30.             logger.error(msg, ex);  
  31.             throw new NestedServletException(msg, ex);  
  32.         }  
  33.   
  34.         // Let subclasses do whatever initialization they like.  
  35.         initFilterBean();  
  36.   
  37.         if (logger.isDebugEnabled()) {  
  38.             logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");  
  39.         }  
  40.     }  

        該方法來自於javax.servlet.Filter,即過濾器的初始化,它的主要工作集中於以下幾行代碼:

[java] view plain copy
  1. // 從properties文件中獲取值,這裏是web.xml  
  2. PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);  
  3. // 設置bean適配器  
  4. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  5. // 設置上下文,這裏的servletContext的設定繼承自ServletContextAware的setter  
  6. ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());  
  7. // 將上下文信息和環境信息設置到bean適配器中,這裏的environment來自於EnvironmentAware的setter  
  8. bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, this.environment));  
  9. // 初始化bean適配器  
  10. initBeanWrapper(bw);  
  11. // 將從properties中獲取的資源放置到bean適配器  
  12. bw.setPropertyValues(pvs, true);  
  13. // 初始化bean  
  14. initFilterBean();  

        其中initFilterBean方法在兩個位置起作用,一處是上文所述的init方法,另一處是afterPropertiesSet方法,在調用該方法前,需要保證用於Filter的所有的bean都已被設置,該方法由子類實現。

        GenericFilterBean中包含一個內部私有類FilterConfigPropertyValues,主要用於將web.xml中定義的init-param的值取出。

        OncePerRequestFilter

        抽象類oncePerRequestFilter繼承自GenericFilterBean,它保留了GenericFilterBean中的所有方法並對之進行了擴展,在oncePerRequestFilter中的主要方法是doFilter,代碼如下:

[java] view plain copy
  1. /** 
  2.      * This <code>doFilter</code> implementation stores a request attribute for 
  3.      * "already filtered", proceeding without filtering again if the 
  4.      * attribute is already there. 
  5.      * @see #getAlreadyFilteredAttributeName 
  6.      * @see #shouldNotFilter 
  7.      * @see #doFilterInternal 
  8.      */  
  9.     public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  10.             throws ServletException, IOException {  
  11.   
  12.         if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  13.             throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  14.         }  
  15.         HttpServletRequest httpRequest = (HttpServletRequest) request;  
  16.         HttpServletResponse httpResponse = (HttpServletResponse) response;  
  17.                 // 調用GenericFilterBean的getFilterName方法返回已過濾的屬性名  
  18.         String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  19.         if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {  
  20.             // 未調用該過濾器或已過濾  
  21.             filterChain.doFilter(request, response);  
  22.         }  
  23.         else {  
  24.             // 進行過濾  
  25.             request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  26.             try {  
  27.                 doFilterInternal(httpRequest, httpResponse, filterChain);  
  28.             }  
  29.             finally {  
  30.                 // Remove the "already filtered" request attribute for this request.  
  31.                 request.removeAttribute(alreadyFilteredAttributeName);  
  32.             }  
  33.         }  
  34.     }  

        在doFilter方法中,doFilterInternal方法由子類實現,主要作用是規定過濾的具體方法。

        AbstractRequestLoggingFilter

        AbstractRequestLoggingFilter繼承了OncePerRequestFilter並實現了其doFilterInternal方法,該方法代碼如下:

[java] view plain copy
  1. /** 
  2.      * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual 
  3.      * request logging both before and after the request is processed. 
  4.      * 
  5.      * @see #beforeRequest 
  6.      * @see #afterRequest 
  7.      */  
  8.     @Override  
  9.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
  10.             throws ServletException, IOException {  
  11.         if (isIncludePayload()) {  
  12.                         // 若日誌中包含負載,則重置request  
  13.             request = new RequestCachingRequestWrapper(request);  
  14.         }  
  15.                 // 過濾前執行的方法  
  16.         beforeRequest(request, getBeforeMessage(request));  
  17.         try {  
  18.                         // 執行過濾  
  19.             filterChain.doFilter(request, response);  
  20.         }  
  21.         finally {  
  22.                         // 過濾後執行的方法  
  23.             afterRequest(request, getAfterMessage(request));  
  24.         }  
  25.     }  

        doFilter方法中的beforeRequest和afterRequest方法由子類實現,RequestCachingRequestWrapper爲AbstractRequestLoggingFilter的內部內,主要作用是重置request。

        區別

         我們在使用過濾器時,通常沒必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防礙我們瞭解這幾個類,就上文所述,

AbstractRequestLoggingFilter繼承自OncePerRequestFilter

OncePerRequestFilter繼承自GenericFilterBean

所以我們知道,genericFilterBean是任何類型的過濾器的一個比較方便的超類,

這個類主要實現的就是從web.xml文件中取得init-param中設定的值,然後對Filter進行初始化(當然,其子類可以覆蓋init方法)

         

OncePerRequestFilter繼承自GenericFilterBean,那麼它自然知道怎麼去獲取配置文件中的屬性及其值,所以其重點不在於取值,而在於確保在接收到一個request後,每個filter只執行一次它的子類只需要關注Filter的具體實現即doFilterInternal


        AbstractRequestLoggingFilter是對OncePerRequestFilter的擴展,它除了遺傳了其父類及祖先類的所有功能外,還在doFilterInternal中決定了在過濾之前和之後執行的事件,它的子類關注的是beforeRequest和afterRequest


        總體來說,這三個類分別執行了Filter的某部分功能,當然,具體如何執行由它們的子類規定,若你需要實現自己的過濾器,也可以根據上文所述繼承你所需要的類



==============================================================================================================

Spring MVC過濾器-上下文信息過濾器(AbstractRequestLoggingFilter及其子類)

http://blog.csdn.net/geloin/article/details/7443329


 AbstractRequestLoggingFilter類定義了兩個方法beforeRequest和afterRequest分別用於設定過濾前後執行的操作

它有三個子類,

分別是CommonsRequestLoggingFilter、

ServletContextRequestLoggingFilter

和Log4jNestedDiagnosticContextFilter,


這三個子類分別實現了各自的beforeRequest和afterRequest。其中,

CommonsRequestLoggingFilter在過濾前後分別打印出一段debug的信息;


ServletContextRequestLoggingFilter在過濾前後分別向日志文件中寫入一段日誌信息,日誌文件可由log4j.properties等指定;

Log4jNestedDiagnosticContextFilter則將日誌信息存儲到NDC中,NDC採用了一個類似棧的機制來push和pot上下文信息,每一個線程都獨立地儲存上下文信息,比如說一個servlet就可以針對 每一個request創建對應的NDC,儲存客戶端地址等信息。


        當然,在AbstractRequestLoggingFilter中,還包含很多其他方法:

        setIncludeQueryString:查詢語句是否包含在日誌文件中,true或false;

        setIncludeClientInfo:客戶地址和session id是否包含在日誌中,true或false;

        setIncludePayload:負載信息是否包含在日誌信息中,true或false;

        setMaxPayloadLength:最大負載量,int值;

        setBeforeMessagePrefix:日誌信息前的信息的前綴;

        setBeforeMessageSuffix:日誌信息前的信息的後綴;

        setAfterMessagePrefix:日誌信息後的信息的前綴;

        setAfterMessageSuffix:日誌信息後的信息的後綴。

        以上這些信息均可以在init-param中進行設置。


       日誌信息在getBeforeMessage和getAfterMessage方法中通過createMessage創建,然後被doFilterInternal調用

日誌信息中至少包含uri信息,其他信息則根據init-param中所設定的值獲取


例如,當includeQueryString的值爲true時,日誌信息中除uri信息外,還包含query string信息。


       由此可以,在spring中已定義的AbstractRequestLoggingFiter的子類過濾器均是用於輸入上下文信息,只不過承載的方式不同


其中CommonsRequestLoggingFilter將上下文信息直接打印

ServletContextRequestLoggingFilter將上下文信息寫入日誌文件

Log4jNestedDiagnosticContextFilter將上下文信息寫入NDC中



==============================================================================================================

Spring MVC過濾器-HiddenHttpMethodFilter

http://blog.csdn.net/geloin/article/details/7444321


因爲很少有人用到GET和POST以外的method, 而且只有部分瀏覽器不支持部分method put,deletede等

但是爲了做到程序兼容性更好,spring3.0添加了一個過濾器HiddenHttpMethodFilter,可以將這些請求轉換爲標準的http方法,使得支持PUT與DELETE,就像可以使用GET、POST、一樣,


 HiddenHttpMethodFilter的父類是OncePerRequestFilter,它繼承了父類的doFilterInternal方法,工作原理是

將jsp頁面的form表單的method屬性值在doFilterInternal方法中轉化爲標準的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、

TRACE,然後到Controller中找到對應的方法。

例如,在使用註解時我們可能會在Controller中用於@RequestMapping(value = "list", method = RequestMethod.PUT),

所以如果你的表單中使用的是<form ...,那麼這個表單會被提交到標了Controller的Method="PUT"的方法中


需要注意的是,由於doFilterInternal方法只對method爲post的表單進行過濾,所以在頁面中必須如下設置:

[html] view plain copy
  1. <form action="..." method="<strong>post</strong>">  
  2.         <input type="hidden" name="_method" value="<strong>put</strong>" />  
  3.         ......  
  4. </form>  

而不是使用:

[html] view plain copy
  1. <form action="..." method="put">  
  2.         ......  
  3. </form>  


 同時,HiddenHttpMethodFilter必須作用於dispatcher前,所以在web.xml中配置HiddenHttpMethodFilter時,需參照如下代碼:

[html] view plain copy
  1.       <filter>    
  2.               <filter-name>HiddenHttpMethodFilter</filter-name>    
  3.               <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>    
  4.       </filter>    
  5.       <filter-mapping>    
  6.               <filter-name>HiddenHttpMethodFilter</filter-name>    
  7.               <servlet-name>spring</servlet-name>    
  8.       </filter-mapping>  
  9.       <servlet>  
  10. <servlet-name>spring</servlet-name>  
  11. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  12. <init-param>  
  13.     <param-name>contextConfigLocation</param-name>  
  14.     <param-value>classpath:dispatcher.xml</param-value>  
  15. </init-param>  
  16. lt;/servlet>  
  17.       <servlet-mapping>  
  18. <servlet-name>spring</servlet-name>  
  19. <url-pattern>*.html</url-pattern>  
  20. lt;/servlet-mapping>  

同樣的,作爲Filter,可以在web.xml中配置HiddenHttpMethodFilter的參數,可配置的參數爲methodParam,

如果不在web.xml裏重新配置的話,default的值爲 “_method”,而他在hidden裏對應的value爲http method的任意一個即可

[html] view plain copy
  1. <input type="hidden" name="_method" value="<strong>put</strong>" />  

如果不想用default的“_method”,可以在web.xml裏重新配置,比如abcd_abc,提供這個修改可能是爲了防止你的form表單裏如果已經有“_method”的定義了,不能用於HiddenHttpMethodFilter實現的目的,通過它可以重寫

[html] view plain copy
  1. <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />  

==============================================================================================================


Spring MVC過濾器-HttpPutFormContentFilter

http://blog.csdn.net/geloin/article/details/7444590


Spring MVC過濾器-HiddenHttpMethodFilter中我們提到,jsp或者說html中的form的method值只能爲post或get,我們可以通過HiddenHttpMethodFilter獲取put表單中的參數-值

而在Spring3.0中獲取put表單的參數-值還有另一種方法,即使用HttpPutFormContentFilter過濾器


 HttpPutFormContentFilter過濾器的作爲就是獲取put表單的值,並將之傳遞到Controller中標註了method爲RequestMethod.put的方法中。

        在web.xml中配置HttpPutFormContentFilter的代碼類似如下:

[html] view plain copy
  1. <filter>  
  2.     <filter-name>httpPutFormcontentFilter</filter-name>  
  3.     <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.     <filter-name>httpPutFormContentFilter</filter-name>  
  7.     <url-pattern>/*</url-pattern>  
  8. </filter-mapping>  

需要注意的是,該過濾器只能接受enctype值爲application/x-www-form-urlencoded的表單,也就是說,在使用該過濾器時,form表單的代碼必須如下

[html] view plain copy
  1. <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">  
  2. ......  
  3. </form>  


==============================================================================================================

ShallowEtagHeaderFilter

ShallowEtagHeaderFilter是spring提供的支持ETag的一個過濾器,所謂ETag是指被請求變量的實體值,是一個可以與Web資源關聯的記號,而Web資源可以是一個Web頁,也可以是JSON或XML文檔,服務器單獨負責判斷記號是什麼及其含義,並在HTTP響應頭中將其傳送到客戶端,以下是服務器端返回的格式:

ETag:"50b1c1d4f775c61:df3"  

客戶端的查詢更新格式是這樣的:

If-None-Match : W / "50b1c1d4f775c61:df3"  

   如果ETag沒改變,則返回狀態304然後不返回,這也和Last-Modified一樣。

   ShallowEtagHeaderFilter會將JSP等的內容緩存,生成MD5的key,然後在response中作爲Etage的header返回給客戶端。下次客戶端對相同的資源(或者說相同的url)發出請求時,客戶端會將之前生成的key作爲If-None-Match的值發送到server端。 Filter會客戶端傳來的值和服務器上的做比較,如果相同,則返回304;否則,將發送新的內容到客戶端。

查看ShallowEtagHeaderFilter的源碼如下:

[java] view plain copy
  1. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{    
  2.   ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);    
  3.   filterChain.doFilter(request, responseWrapper);    
  4.   // 由此可知,服務器仍會處理請求    
  5.   byte[] body = responseWrapper.toByteArray();    
  6.   int statusCode = responseWrapper.getStatusCode();    
  7.      
  8.   if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {    
  9.     String responseETag = generateETagHeaderValue(body);    
  10.     response.setHeader(HEADER_ETAG, responseETag);    
  11.      
  12.     String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);    
  13.     if (responseETag.equals(requestETag)) {    
  14.       if (this.logger.isTraceEnabled()) {    
  15.         this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");    
  16.       }    
  17.       response.setStatus(304);    
  18.     }    
  19.     else {    
  20.       if (this.logger.isTraceEnabled()) {    
  21.         this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");    
  22.       }    
  23.       copyBodyToResponse(body, response);    
  24.     }    
  25.   }    
  26.   else {    
  27.     if (this.logger.isTraceEnabled()) {    
  28.       this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");    
  29.     }    
  30.     copyBodyToResponse(body, response);    
  31.   }    
  32. }    

由源碼可知,ShallowEtagHeaderFilter只能根據結果判斷是否重新向客戶端發送數據,並不會不處理請求,因此節省帶寬,而不能提高服務器性能。

配置ShallowEtagHeaderFilter的代碼如下:


[html] view plain copy
  1. <filter>      
  2.    <filter-name>shallowEtagHeaderFilter</filter-name>      
  3.    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>      
  4. </filter>      
  5. <filter-mapping>      
  6.    <filter-name>shallowEtagHeaderFilter</filter-name>      
  7.    <servlet-name>spring</servlet-name>      
  8. </filter-mapping>    


RequestContextFilter

這是在Spring2.0時添加的類,通過LocaleContextHolder和RequestContextHolder把Http request對象基於LocalThread綁定到請求提供服務的線程上。現在一般使用DispatcherServlet這個中央分發器。現在RequestContextFilter過濾器主要用於第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用該過濾器配置。配置如下:


[html] view plain copy
  1. <filter>      
  2.     <filter-name>RequestContextFilter</filter-name>      
  3.     <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>      
  4. </filter>      
  5. <filter-mapping>      
  6.     <filter-name>RequestContextFilter</filter-name>      
  7.     <servlet-name>Faces Servlet</servlet-name>      
  8. </filter-mapping>    




http://liuluo129.iteye.com/blog/1965268

圖來自上面連接


http://blog.csdn.net/geloin/article/details/7441330

整理於上面


Spring的web包中中有很多過濾器,這些過濾器位於org.springframework.web.filter並且理所當然地實現了javax.servlet.Filter,不過實現的方式有以下幾類:

        (1) 直接實現Filter,這一類過濾器只有CompositeFilter;

        (2) 繼承抽象類GenericFilterBean,該類實現了javax.servlet.Filter,這一類的過濾器只有一個,即DelegatingFilterProxy;

        (3) 繼承抽象類OncePerRequestFilter,該類爲GenericFilterBean的直接子類,這一類過濾器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;

        (4) 繼承抽象類AbstractRequestLoggingFilter,該類爲OncePerRequestFilter的直接子類,這一類過濾器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。

        本文要講述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。

        GenericFilterBean

        抽象類GenericFilterBean實現了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五個接口,作用如下:

        (1) Filter,實現過濾器;

        (2) BeanNameAware,實現該接口的setBeanName方法,便於Bean管理器生成Bean;

        (3) EnvironmentAware,實現該接口的setEnvironment方法,指明該Bean運行的環境;

        (4) ServletContextAware,實現該接口的setServletContextAware方法,指明上下文;

        (5) InitializingBean,實現該接口的afterPropertiesSet方法,指明設置屬性生的操作;

        (6) DisposableBean,實現該接口的destroy方法,用於回收資源。

        GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在該類中實現,doFilter在具體實現類中實現。init的代碼如下:

[java] view plain copy
  1. /** 
  2.      * Standard way of initializing this filter. 
  3.      * Map config parameters onto bean properties of this filter, and 
  4.      * invoke subclass initialization. 
  5.      * @param filterConfig the configuration for this filter 
  6.      * @throws ServletException if bean properties are invalid (or required 
  7.      * properties are missing), or if subclass initialization fails. 
  8.      * @see #initFilterBean 
  9.      */  
  10.     public final void init(FilterConfig filterConfig) throws ServletException {  
  11.         Assert.notNull(filterConfig, "FilterConfig must not be null");  
  12.         if (logger.isDebugEnabled()) {  
  13.             logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");  
  14.         }  
  15.   
  16.         this.filterConfig = filterConfig;  
  17.   
  18.         // Set bean properties from init parameters.  
  19.         try {  
  20.             PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);  
  21.             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  22.             ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());  
  23.             bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, this.environment));  
  24.             initBeanWrapper(bw);  
  25.             bw.setPropertyValues(pvs, true);  
  26.         }  
  27.         catch (BeansException ex) {  
  28.             String msg = "Failed to set bean properties on filter '" +  
  29.                 filterConfig.getFilterName() + "': " + ex.getMessage();  
  30.             logger.error(msg, ex);  
  31.             throw new NestedServletException(msg, ex);  
  32.         }  
  33.   
  34.         // Let subclasses do whatever initialization they like.  
  35.         initFilterBean();  
  36.   
  37.         if (logger.isDebugEnabled()) {  
  38.             logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");  
  39.         }  
  40.     }  

        該方法來自於javax.servlet.Filter,即過濾器的初始化,它的主要工作集中於以下幾行代碼:

[java] view plain copy
  1. // 從properties文件中獲取值,這裏是web.xml  
  2. PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);  
  3. // 設置bean適配器  
  4. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  5. // 設置上下文,這裏的servletContext的設定繼承自ServletContextAware的setter  
  6. ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());  
  7. // 將上下文信息和環境信息設置到bean適配器中,這裏的environment來自於EnvironmentAware的setter  
  8. bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, this.environment));  
  9. // 初始化bean適配器  
  10. initBeanWrapper(bw);  
  11. // 將從properties中獲取的資源放置到bean適配器  
  12. bw.setPropertyValues(pvs, true);  
  13. // 初始化bean  
  14. initFilterBean();  

        其中initFilterBean方法在兩個位置起作用,一處是上文所述的init方法,另一處是afterPropertiesSet方法,在調用該方法前,需要保證用於Filter的所有的bean都已被設置,該方法由子類實現。

        GenericFilterBean中包含一個內部私有類FilterConfigPropertyValues,主要用於將web.xml中定義的init-param的值取出。

        OncePerRequestFilter

        抽象類oncePerRequestFilter繼承自GenericFilterBean,它保留了GenericFilterBean中的所有方法並對之進行了擴展,在oncePerRequestFilter中的主要方法是doFilter,代碼如下:

[java] view plain copy
  1. /** 
  2.      * This <code>doFilter</code> implementation stores a request attribute for 
  3.      * "already filtered", proceeding without filtering again if the 
  4.      * attribute is already there. 
  5.      * @see #getAlreadyFilteredAttributeName 
  6.      * @see #shouldNotFilter 
  7.      * @see #doFilterInternal 
  8.      */  
  9.     public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  10.             throws ServletException, IOException {  
  11.   
  12.         if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  13.             throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  14.         }  
  15.         HttpServletRequest httpRequest = (HttpServletRequest) request;  
  16.         HttpServletResponse httpResponse = (HttpServletResponse) response;  
  17.                 // 調用GenericFilterBean的getFilterName方法返回已過濾的屬性名  
  18.         String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  19.         if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {  
  20.             // 未調用該過濾器或已過濾  
  21.             filterChain.doFilter(request, response);  
  22.         }  
  23.         else {  
  24.             // 進行過濾  
  25.             request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  26.             try {  
  27.                 doFilterInternal(httpRequest, httpResponse, filterChain);  
  28.             }  
  29.             finally {  
  30.                 // Remove the "already filtered" request attribute for this request.  
  31.                 request.removeAttribute(alreadyFilteredAttributeName);  
  32.             }  
  33.         }  
  34.     }  

        在doFilter方法中,doFilterInternal方法由子類實現,主要作用是規定過濾的具體方法。

        AbstractRequestLoggingFilter

        AbstractRequestLoggingFilter繼承了OncePerRequestFilter並實現了其doFilterInternal方法,該方法代碼如下:

[java] view plain copy
  1. /** 
  2.      * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual 
  3.      * request logging both before and after the request is processed. 
  4.      * 
  5.      * @see #beforeRequest 
  6.      * @see #afterRequest 
  7.      */  
  8.     @Override  
  9.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
  10.             throws ServletException, IOException {  
  11.         if (isIncludePayload()) {  
  12.                         // 若日誌中包含負載,則重置request  
  13.             request = new RequestCachingRequestWrapper(request);  
  14.         }  
  15.                 // 過濾前執行的方法  
  16.         beforeRequest(request, getBeforeMessage(request));  
  17.         try {  
  18.                         // 執行過濾  
  19.             filterChain.doFilter(request, response);  
  20.         }  
  21.         finally {  
  22.                         // 過濾後執行的方法  
  23.             afterRequest(request, getAfterMessage(request));  
  24.         }  
  25.     }  

        doFilter方法中的beforeRequest和afterRequest方法由子類實現,RequestCachingRequestWrapper爲AbstractRequestLoggingFilter的內部內,主要作用是重置request。

        區別

         我們在使用過濾器時,通常沒必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防礙我們瞭解這幾個類,就上文所述,

AbstractRequestLoggingFilter繼承自OncePerRequestFilter

OncePerRequestFilter繼承自GenericFilterBean

所以我們知道,genericFilterBean是任何類型的過濾器的一個比較方便的超類,

這個類主要實現的就是從web.xml文件中取得init-param中設定的值,然後對Filter進行初始化(當然,其子類可以覆蓋init方法)

         

OncePerRequestFilter繼承自GenericFilterBean,那麼它自然知道怎麼去獲取配置文件中的屬性及其值,所以其重點不在於取值,而在於確保在接收到一個request後,每個filter只執行一次它的子類只需要關注Filter的具體實現即doFilterInternal


        AbstractRequestLoggingFilter是對OncePerRequestFilter的擴展,它除了遺傳了其父類及祖先類的所有功能外,還在doFilterInternal中決定了在過濾之前和之後執行的事件,它的子類關注的是beforeRequest和afterRequest


        總體來說,這三個類分別執行了Filter的某部分功能,當然,具體如何執行由它們的子類規定,若你需要實現自己的過濾器,也可以根據上文所述繼承你所需要的類



==============================================================================================================

Spring MVC過濾器-上下文信息過濾器(AbstractRequestLoggingFilter及其子類)

http://blog.csdn.net/geloin/article/details/7443329


 AbstractRequestLoggingFilter類定義了兩個方法beforeRequest和afterRequest分別用於設定過濾前後執行的操作

它有三個子類,

分別是CommonsRequestLoggingFilter、

ServletContextRequestLoggingFilter

和Log4jNestedDiagnosticContextFilter,


這三個子類分別實現了各自的beforeRequest和afterRequest。其中,

CommonsRequestLoggingFilter在過濾前後分別打印出一段debug的信息;


ServletContextRequestLoggingFilter在過濾前後分別向日志文件中寫入一段日誌信息,日誌文件可由log4j.properties等指定;

Log4jNestedDiagnosticContextFilter則將日誌信息存儲到NDC中,NDC採用了一個類似棧的機制來push和pot上下文信息,每一個線程都獨立地儲存上下文信息,比如說一個servlet就可以針對 每一個request創建對應的NDC,儲存客戶端地址等信息。


        當然,在AbstractRequestLoggingFilter中,還包含很多其他方法:

        setIncludeQueryString:查詢語句是否包含在日誌文件中,true或false;

        setIncludeClientInfo:客戶地址和session id是否包含在日誌中,true或false;

        setIncludePayload:負載信息是否包含在日誌信息中,true或false;

        setMaxPayloadLength:最大負載量,int值;

        setBeforeMessagePrefix:日誌信息前的信息的前綴;

        setBeforeMessageSuffix:日誌信息前的信息的後綴;

        setAfterMessagePrefix:日誌信息後的信息的前綴;

        setAfterMessageSuffix:日誌信息後的信息的後綴。

        以上這些信息均可以在init-param中進行設置。


       日誌信息在getBeforeMessage和getAfterMessage方法中通過createMessage創建,然後被doFilterInternal調用

日誌信息中至少包含uri信息,其他信息則根據init-param中所設定的值獲取


例如,當includeQueryString的值爲true時,日誌信息中除uri信息外,還包含query string信息。


       由此可以,在spring中已定義的AbstractRequestLoggingFiter的子類過濾器均是用於輸入上下文信息,只不過承載的方式不同


其中CommonsRequestLoggingFilter將上下文信息直接打印

ServletContextRequestLoggingFilter將上下文信息寫入日誌文件

Log4jNestedDiagnosticContextFilter將上下文信息寫入NDC中



==============================================================================================================

Spring MVC過濾器-HiddenHttpMethodFilter

http://blog.csdn.net/geloin/article/details/7444321


因爲很少有人用到GET和POST以外的method, 而且只有部分瀏覽器不支持部分method put,deletede等

但是爲了做到程序兼容性更好,spring3.0添加了一個過濾器HiddenHttpMethodFilter,可以將這些請求轉換爲標準的http方法,使得支持PUT與DELETE,就像可以使用GET、POST、一樣,


 HiddenHttpMethodFilter的父類是OncePerRequestFilter,它繼承了父類的doFilterInternal方法,工作原理是

將jsp頁面的form表單的method屬性值在doFilterInternal方法中轉化爲標準的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、

TRACE,然後到Controller中找到對應的方法。

例如,在使用註解時我們可能會在Controller中用於@RequestMapping(value = "list", method = RequestMethod.PUT),

所以如果你的表單中使用的是<form ...,那麼這個表單會被提交到標了Controller的Method="PUT"的方法中


需要注意的是,由於doFilterInternal方法只對method爲post的表單進行過濾,所以在頁面中必須如下設置:

[html] view plain copy
  1. <form action="..." method="<strong>post</strong>">  
  2.         <input type="hidden" name="_method" value="<strong>put</strong>" />  
  3.         ......  
  4. </form>  

而不是使用:

[html] view plain copy
  1. <form action="..." method="put">  
  2.         ......  
  3. </form>  


 同時,HiddenHttpMethodFilter必須作用於dispatcher前,所以在web.xml中配置HiddenHttpMethodFilter時,需參照如下代碼:

[html] view plain copy
  1.       <filter>    
  2.               <filter-name>HiddenHttpMethodFilter</filter-name>    
  3.               <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>    
  4.       </filter>    
  5.       <filter-mapping>    
  6.               <filter-name>HiddenHttpMethodFilter</filter-name>    
  7.               <servlet-name>spring</servlet-name>    
  8.       </filter-mapping>  
  9.       <servlet>  
  10. <servlet-name>spring</servlet-name>  
  11. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  12. <init-param>  
  13.     <param-name>contextConfigLocation</param-name>  
  14.     <param-value>classpath:dispatcher.xml</param-value>  
  15. </init-param>  
  16. lt;/servlet>  
  17.       <servlet-mapping>  
  18. <servlet-name>spring</servlet-name>  
  19. <url-pattern>*.html</url-pattern>  
  20. lt;/servlet-mapping>  

同樣的,作爲Filter,可以在web.xml中配置HiddenHttpMethodFilter的參數,可配置的參數爲methodParam,

如果不在web.xml裏重新配置的話,default的值爲 “_method”,而他在hidden裏對應的value爲http method的任意一個即可

[html] view plain copy
  1. <input type="hidden" name="_method" value="<strong>put</strong>" />  

如果不想用default的“_method”,可以在web.xml裏重新配置,比如abcd_abc,提供這個修改可能是爲了防止你的form表單裏如果已經有“_method”的定義了,不能用於HiddenHttpMethodFilter實現的目的,通過它可以重寫

[html] view plain copy
  1. <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />  

==============================================================================================================


Spring MVC過濾器-HttpPutFormContentFilter

http://blog.csdn.net/geloin/article/details/7444590


Spring MVC過濾器-HiddenHttpMethodFilter中我們提到,jsp或者說html中的form的method值只能爲post或get,我們可以通過HiddenHttpMethodFilter獲取put表單中的參數-值

而在Spring3.0中獲取put表單的參數-值還有另一種方法,即使用HttpPutFormContentFilter過濾器


 HttpPutFormContentFilter過濾器的作爲就是獲取put表單的值,並將之傳遞到Controller中標註了method爲RequestMethod.put的方法中。

        在web.xml中配置HttpPutFormContentFilter的代碼類似如下:

[html] view plain copy
  1. <filter>  
  2.     <filter-name>httpPutFormcontentFilter</filter-name>  
  3.     <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.     <filter-name>httpPutFormContentFilter</filter-name>  
  7.     <url-pattern>/*</url-pattern>  
  8. </filter-mapping>  

需要注意的是,該過濾器只能接受enctype值爲application/x-www-form-urlencoded的表單,也就是說,在使用該過濾器時,form表單的代碼必須如下

[html] view plain copy
  1. <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">  
  2. ......  
  3. </form>  


==============================================================================================================

ShallowEtagHeaderFilter

ShallowEtagHeaderFilter是spring提供的支持ETag的一個過濾器,所謂ETag是指被請求變量的實體值,是一個可以與Web資源關聯的記號,而Web資源可以是一個Web頁,也可以是JSON或XML文檔,服務器單獨負責判斷記號是什麼及其含義,並在HTTP響應頭中將其傳送到客戶端,以下是服務器端返回的格式:

ETag:"50b1c1d4f775c61:df3"  

客戶端的查詢更新格式是這樣的:

If-None-Match : W / "50b1c1d4f775c61:df3"  

   如果ETag沒改變,則返回狀態304然後不返回,這也和Last-Modified一樣。

   ShallowEtagHeaderFilter會將JSP等的內容緩存,生成MD5的key,然後在response中作爲Etage的header返回給客戶端。下次客戶端對相同的資源(或者說相同的url)發出請求時,客戶端會將之前生成的key作爲If-None-Match的值發送到server端。 Filter會客戶端傳來的值和服務器上的做比較,如果相同,則返回304;否則,將發送新的內容到客戶端。

查看ShallowEtagHeaderFilter的源碼如下:

[java] view plain copy
  1. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{    
  2.   ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);    
  3.   filterChain.doFilter(request, responseWrapper);    
  4.   // 由此可知,服務器仍會處理請求    
  5.   byte[] body = responseWrapper.toByteArray();    
  6.   int statusCode = responseWrapper.getStatusCode();    
  7.      
  8.   if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {    
  9.     String responseETag = generateETagHeaderValue(body);    
  10.     response.setHeader(HEADER_ETAG, responseETag);    
  11.      
  12.     String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);    
  13.     if (responseETag.equals(requestETag)) {    
  14.       if (this.logger.isTraceEnabled()) {    
  15.         this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");    
  16.       }    
  17.       response.setStatus(304);    
  18.     }    
  19.     else {    
  20.       if (this.logger.isTraceEnabled()) {    
  21.         this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");    
  22.       }    
  23.       copyBodyToResponse(body, response);    
  24.     }    
  25.   }    
  26.   else {    
  27.     if (this.logger.isTraceEnabled()) {    
  28.       this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");    
  29.     }    
  30.     copyBodyToResponse(body, response);    
  31.   }    
  32. }    

由源碼可知,ShallowEtagHeaderFilter只能根據結果判斷是否重新向客戶端發送數據,並不會不處理請求,因此節省帶寬,而不能提高服務器性能。

配置ShallowEtagHeaderFilter的代碼如下:


[html] view plain copy
  1. <filter>      
  2.    <filter-name>shallowEtagHeaderFilter</filter-name>      
  3.    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>      
  4. </filter>      
  5. <filter-mapping>      
  6.    <filter-name>shallowEtagHeaderFilter</filter-name>      
  7.    <servlet-name>spring</servlet-name>      
  8. </filter-mapping>    


RequestContextFilter

這是在Spring2.0時添加的類,通過LocaleContextHolder和RequestContextHolder把Http request對象基於LocalThread綁定到請求提供服務的線程上。現在一般使用DispatcherServlet這個中央分發器。現在RequestContextFilter過濾器主要用於第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用該過濾器配置。配置如下:


[html] view plain copy
  1. <filter>      
  2.     <filter-name>RequestContextFilter</filter-name>      
  3.     <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>      
  4. </filter>      
  5. <filter-mapping>      
  6.     <filter-name>RequestContextFilter</filter-name>      
  7.     <servlet-name>Faces Servlet</servlet-name>      
  8. </filter-mapping>    



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章