Filter使用方式及源码解析

Posted notulnix

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Filter使用方式及源码解析相关的知识,希望对你有一定的参考价值。

如何使用Filter

先看下filter接口。

package javax.servlet;

import java.io.IOException;

/**
 * A filter is an object that performs filtering tasks on either the request to
 * a resource (a servlet or static content), or on the response from a resource,
 * or both. <br>
 * <br>
 * Filters perform filtering in the <code>doFilter</code> method. Every Filter
 * has access to a FilterConfig object from which it can obtain its
 * initialization parameters, a reference to the ServletContext which it can
 * use, for example, to load resources needed for filtering tasks.
 * <p>
 * Filters are configured in the deployment descriptor of a web application
 * <p>
 * Examples that have been identified for this design are<br>
 * 1) Authentication Filters <br>
 * 2) Logging and Auditing Filters <br>
 * 3) Image conversion Filters <br>
 * 4) Data compression Filters <br>
 * 5) Encryption Filters <br>
 * 6) Tokenizing Filters <br>
 * 7) Filters that trigger resource access events <br>
 * 8) XSL/T filters <br>
 * 9) Mime-type chain Filter <br>
 *
 * @since Servlet 2.3
 */
public interface Filter 

    /**
     * Called by the web container to indicate to a filter that it is being
     * placed into service. The servlet container calls the init method exactly
     * once after instantiating the filter. The init method must complete
     * successfully before the filter is asked to do any filtering work.
     * <p>
     * The web container cannot place the filter into service if the init method
     * either:
     * <ul>
     * <li>Throws a ServletException</li>
     * <li>Does not return within a time period defined by the web
     *     container</li>
     * </ul>
     * The default implementation is a NO-OP.
     *
     * @param filterConfig The configuration information associated with the
     *                     filter instance being initialised
     *
     * @throws ServletException if the initialisation fails
     */
    public default void init(FilterConfig filterConfig) throws ServletException 

    /**
     * The <code>doFilter</code> method of the Filter is called by the container
     * each time a request/response pair is passed through the chain due to a
     * client request for a resource at the end of the chain. The FilterChain
     * passed in to this method allows the Filter to pass on the request and
     * response to the next entity in the chain.
     * <p>
     * A typical implementation of this method would follow the following
     * pattern:- <br>
     * 1. Examine the request<br>
     * 2. Optionally wrap the request object with a custom implementation to
     * filter content or headers for input filtering <br>
     * 3. Optionally wrap the response object with a custom implementation to
     * filter content or headers for output filtering <br>
     * 4. a) <strong>Either</strong> invoke the next entity in the chain using
     * the FilterChain object (<code>chain.doFilter()</code>), <br>
     * 4. b) <strong>or</strong> not pass on the request/response pair to the
     * next entity in the filter chain to block the request processing<br>
     * 5. Directly set headers on the response after invocation of the next
     * entity in the filter chain.
     *
     * @param request  The request to process
     * @param response The response associated with the request
     * @param chain    Provides access to the next filter in the chain for this
     *                 filter to pass the request and response to for further
     *                 processing
     *
     * @throws IOException if an I/O error occurs during this filter's
     *                     processing of the request
     * @throws ServletException if the processing fails for any other reason
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    /**
     * Called by the web container to indicate to a filter that it is being
     * taken out of service. This method is only called once all threads within
     * the filter's doFilter method have exited or after a timeout period has
     * passed. After the web container calls this method, it will not call the
     * doFilter method again on this instance of the filter. <br>
     * <br>
     *
     * This method gives the filter an opportunity to clean up any resources
     * that are being held (for example, memory, file handles, threads) and make
     * sure that any persistent state is synchronized with the filter's current
     * state in memory.
     *
     * The default implementation is a NO-OP.
     */
    public default void destroy() 

使用filter的方式很简单,首先编写一个类实现Filter接口,在doFilter中进行拦截操作,参数request和response由connector生成,层层传递过来,FilterChain就是当前filter所在的拦截链,filter的doFilter函数也是由FilterChain调用的。以下的代码展示了如何修改request中body的字符集。

public class CharacterEncodingFilter implements Filter 

    private FilterConfig filterConfig;
    private String defaultCharset = "UTF-8";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException 
        
        String charset = this.filterConfig.getInitParameter("charset");
        if(charset == null)
            charset = defaultCharset;
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse rep = (HttpServletResponse) response;
        
        req.setCharacterEncoding(charset);
        rep.setCharacterEncoding(charset);
        rep.setContentType("text/html;charset="+charset);
        
        chain.doFilter(request, response);
    

    @Override
    public void destroy() 
    

    @Override
    public void init(FilterConfig filterConfig) throws ServletException 
        this.filterConfig = filterConfig;
    

接着在web.xml中配置filter。

  <filter>
    <filter-name>FilterDemo2</filter-name>
    <filter-class>nagi.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>charset</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>FilterDemo2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

request.setCharacterEncoding函数只能更改请求 body的文本编码,不能改变请求网址的编码。

Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader(). Otherwise, it has no effect.

Filter从生到死

FilterChain构造

当请求传递到servlet对应的StandardWrapperValve类时,如果当前的servlet可以被访问,那么会分配一个servlet实例并构造一个ApplicationFilterChain来处理这次请求。

创建一个ApplicationFilterChain实例后,会设置对应的servlet,将当前servletContext中的FilterMap数组取出,如果匹配请求的dispatch模式和路径,或者是匹配请求的dispatch模式和servlet的名称,就会将FilterMap对应的ApplicationFilterConfig加入到ApplicationFilterChain中。FilterMapApplicationFilterConfig都存储在servletContext中。

FilterChain使用

Filter由对应的FilterConfig获得,ApplicationFilterChain的第一次DoFilterStandardWrapperValve调用,每次调用DoFilterApplicationFilterChain中的index便会指向下一个FilterMap。如果没有index>=FilterMap数组的长度,便会调用对应servlet类的service函数。

在这里除了第一次调用DoFilter由ApplicationFilterChain代码执行,其余的DoFilter都需要由Filter调用。

FilterMap和FilterConfig的加载

当Tomcat启动的时候,StandardHost下的StandardContext会调用初始化器的OnStartUp()函数,触发ContextConfig的Start事件,接着回去调用Digester类解析web.xml文件,Digester类会通过反射调用WebXml.addFilterWebXml.addFilterMapping函数,将FilterDef和filterMap添加到WebXml的实例中。合并完WebXml后,会检验每个filterMap的合法性,并将其添加到StandardContext的filterMap数组中。

如果配置都合法,那么StandardContext会调用filterStart函数,将StandardContext和filterDefs注册到ApplicationFilterConfig中,并将其添加到filterConfigs Map中去。可以通过filterMap的name从filterConfig中获得对应的filterDef。

FilterChain中Filter的顺序

同一个web.xml的filter在FilterChain中的顺序由filterMapping标签的顺序决定,因为代码中生成FilterConfig,再添加到FilterChain的操作是遍历FilterMapping数组完成的,FilterMapping数组中Filter的顺序决定了FilterChain中Filter的调用顺序。

addFilter处理的是以下xml标记

  <filter>
    <filter-name>FilterDemo2</filter-name>
    <filter-class>nagi.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>charset</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter>

addFilterMapping处理如下xml标记

  <filter-mapping>
    <filter-name>FilterDemo2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

以上是关于Filter使用方式及源码解析的主要内容,如果未能解决你的问题,请参考以下文章

10-9 Zuul核心之预定义Filter讲解及源码解析思路介绍

10-9 Zuul核心之预定义Filter讲解及源码解析思路介绍

SpringCloud-Zuul(二):自定义Filter及Zuul内部路由源码解析

Django-Filter源码解析一

Axios使用及源码解析

GBDT原理详解及sklearn源码解析