美文网首页
重新认识javax.servlet.Filter过滤器

重新认识javax.servlet.Filter过滤器

作者: _palm | 来源:发表于2018-01-09 17:15 被阅读395次

    在web容器中, 最先加载的是 Listener, 其次是 Filter 最后是 Servlet.
    Filter是一个特殊的 Servlet, 其生命周期分为以下三部分:

    • init(FilterConfig filterConfig) // 为Filter初始化 提供支持
    • doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //这个是 Filter的核心方法被拦截请求在这里被处理
    • destroy() // Filter 实例销毁前的准备工作 //Called by the web container to indicate to a filter that it is being taken out of service.

    通常我们将自定义的Filter配置在web容器的web.xml中, 大致分为两个部分来配置这个Filter.

    //声明filter
    <filter>
        <filter-name>filter-name</filter-name>
        <filter-class>com.xx.FilterClass</filter-class>
    //初始化参数
        <init-param>
            <param-name>key</param-name>
            <param-value>value</param-value>
        </init-param>
    </filter>
    
    //filter 拦截规则
    <filter-mapping>
      <filter-mapping>
      <filter-name>filter-name</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    和servlet不同的是, 如果配置多个filter, 则其声明的先后顺序是区别的, servlet在匹配到后流程至此终止, 而 filter不一定, 他是可以向下传递下去进入下一个filter. 这里能够使 Filter形成链式调用关键点在于 doFilter方法参数FilterChain {doFilter(ServletRequest request, ServletResponse response, FilterChain chain)}

    FilterChain 是一个接口, 他只有一个方法 doFilter(ServletRequest var1, ServletResponse var2). 在我们希望将 #request和 #response传递给下一个 Filter处理的时候就会使用 FilterChain. 如:

    filterChain.doFilter(request, response) ;
    

    这样就会进入下一个Filter的处理逻辑. 如此形成有名的Filter职责链. 类似链表结构. 链表是持有自己的引用, 但是FilterChain不是, 他有点类似代理和链表的结合体. 经过查看 tomcat源码, 才知道, 这个职责链是由web容器管理的. 不同的容器实现FilterChain接口方法 #doFilter 就可以了. 例如在tomcat中, 实现类叫ApplicationFilterChain 这个类包含我们声明的全部Filter, 并且有序, 然后记录当前Filter在Filter链中的索引值, 获取该索引值的Filter然后调用doFilter同时将自己传给这个Filter, 代码如下:

    filter.doFilter(request, response, this);
    

    如果全部Filter执行完毕, 则将流程交给对应的servlet实例执行具体的业务.

    servlet.service(request, response);
    

    至此全部流程完毕.

    我接触到的这种职责链模式很多地方有使用, spring#CompositeFilter就是职责链很经典的使用而且也重新实现了一个简单的职责链管理器. 在 spring-web模块中可以看见:

    private static class VirtualFilterChain implements FilterChain {
    
       private final FilterChain originalChain;
    
       private final List<? extends Filter> additionalFilters;
    
       private int currentPosition = 0;
    
       public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
          this.originalChain = chain;
          this.additionalFilters = additionalFilters;
       }
    
       @Override
       public void doFilter(final ServletRequest request, final ServletResponse response)
             throws IOException, ServletException {
    
          if (this.currentPosition == this.additionalFilters.size()) {
             this.originalChain.doFilter(request, response);
          }
          else {
             this.currentPosition++;
             Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
             nextFilter.doFilter(request, response, this);
          }
       }
    }
    

    根据描述, CompositeFilter通常需要结合 FIlter代理类#DelegatingFilterProxy

    /**
     * A generic composite servlet {@link Filter} that just delegates its behavior
     * to a chain (list) of user-supplied filters, achieving the functionality of a
     * {@link FilterChain}, but conveniently using only {@link Filter} instances.
     *
     * <p>This is useful for filters that require dependency injection, and can
     * therefore be set up in a Spring application context. Typically, this
     * composite would be used in conjunction with {@link DelegatingFilterProxy},
     * so that it can be declared in Spring but applied to a servlet context.
     *
     * @author Dave Syer
     * @since 3.1
     */
    

    这个类我们配置在web.xml文件中, 作为对外唯一可见的一个Filter. 如:

    <filter>
        <filter-name>compositeFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>compositeFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    查看 DelegatingFilterProxy源码, DelegatingFilterProxy会在spring容器bean初始化完成之后初始化被代理的Filter.

    public class DelegatingFilterProxy extends GenericFilterBean{
    
    @Override
    protected void initFilterBean() throws ServletException {
       synchronized (this.delegateMonitor) {
          if (this.delegate == null) {
             // If no target bean name specified, use filter name.
             if (this.targetBeanName == null) {
                this.targetBeanName = getFilterName();
             }
             // Fetch Spring root application context and initialize the delegate early,
             // if possible. If the root application context will be started after this
             // filter proxy, we'll have to resort to lazy initialization.
             WebApplicationContext wac = findWebApplicationContext();
             if (wac != null) {
                this.delegate = initDelegate(wac);
             }
          }
       }
    }
    }
    
    public abstract class GenericFilterBean implements
          Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {
    
    /**
     * Calls the {@code initFilterBean()} method that might
     * contain custom initialization of a subclass.
     * <p>Only relevant in case of initialization as bean, where the
     * standard {@code init(FilterConfig)} method won't be called.
     * @see #initFilterBean()
     * @see #init(javax.servlet.FilterConfig)
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
       initFilterBean(); //这是一个空方法, 需要子类实现
    }
    }
    

    这样就形成了Filter代理 和Filter职责链.
    使用DelegatingFilterProxy的目的多用于我们在自定义业务FIlter的时候常常需要依赖spring管理的bean. 但是因为在web容器中, Filter首先被加载, 这之后spring容器才会被加载(这里还没有代码验证过),所以如果在Filter中依赖spring管理bean则会出现空指针引用, 无法使用. 当使用DelegatingFilterProxy后, 这种情况就改变了. 根据这个类的实现. 容器加载DelegatingFilterProxy这个Filter后, 会等待spring容器初始化全部bean完成后#afterPropertiesSet方法被调用的时候才会初始化DelegatingFilterProxy这个Filter代理的全部Filter, 这时候被代理Filter就能够拿到spring容器里的全部bean.

    上面的说法, 我觉得不对, DelegatingFilterProxy 首先可以将多个Filter串联在一起, 多个Filter可以以Spring Bean的形式的定义在spring配置文件中, 而不用全部声明到web.xml文件中. 然后这些Filter初始化都委托给DelegatingFilterProxy. 我觉得这是这个类本来的职责.
    按照web容器规范, Listener/FIlter/Servlet他们在web容器中的的初始化顺序是 Listener -> FIlter -> Servlet ; 如果引入spring框架, 一般, 我们都是使用 Spring#ContextLoaderListener来加载spring以及初始化Spring IOC容器, 所以 这个先后顺序没有问题, 只是Filter中如果依赖了spring bean拿不到bean. 无法注入, 因为web.xml中无法注入spring bean, Filter也拿不到ApplicationContext, 所以没有办法拿到spring#bean, 只有把 Filter也纳入spring管理这样才可以拿到spring bean.

    注意: 以下配置节点targetFilterLifecycle

    <filter>
        <filter-name>compositeFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    

    targetFilterLifecycle为 true代表spring管理filter的初始化, 即 init和 destroy方法在适当的时候会被spring调用. 如果为false则这两个方法无效.具体代码如下:

    /**
     * Initialize the Filter delegate, defined as bean the given Spring
     * application context.
     * <p>The default implementation fetches the bean from the application context
     * and calls the standard {@code Filter.init} method on it, passing
     * in the FilterConfig of this Filter proxy.
     * @param wac the root application context
     * @return the initialized delegate Filter
     * @throws ServletException if thrown by the Filter
     * @see #getTargetBeanName()
     * @see #isTargetFilterLifecycle()
     * @see #getFilterConfig()
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
       Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
       if (isTargetFilterLifecycle()) {
          delegate.init(getFilterConfig());
       }
       return delegate;
    }
    
    /**
     * Destroy the Filter delegate.
     * Default implementation simply calls {@code Filter.destroy} on it.
     * @param delegate the Filter delegate (never {@code null})
     * @see #isTargetFilterLifecycle()
     * @see javax.servlet.Filter#destroy()
     */
    protected void destroyDelegate(Filter delegate) {
       if (isTargetFilterLifecycle()) {
          delegate.destroy();
       }
    }
    

    相关文章

      网友评论

          本文标题:重新认识javax.servlet.Filter过滤器

          本文链接:https://www.haomeiwen.com/subject/vqdgnxtx.html