• spring 过滤器


    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的代码如下:

    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,即过滤器的初始化,它的主要工作集中于以下几行代码:

     

     

    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,代码如下:

    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方法,该方法代码如下:

     

    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的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类。


  • 相关阅读:
    Spark Streaming 调优指南
    Hive实战之Youtube数据集
    【源码解析】BlockManager详解
    Spark操作HBase问题:java.io.IOException: Non-increasing Bloom keys
    Spark实战之读写HBase
    ZooKeeper的简单理解
    Flume-ng源码解析之Source组件
    Flume-ng源码解析之Sink组件
    Flume-ng源码解析之Channel组件
    Flume-ng源码解析之启动流程
  • 原文地址:https://www.cnblogs.com/signheart/p/6609641.html
Copyright © 2020-2023  润新知