过滤器Filter

过滤器filter简介

Filter 是 Servlet 规范的三大组件之一,另外两个分别是servlet和listener。filter中文意思是过滤,可以在请求到达目标资源之前先对请求进行拦截过滤,即对请求进行一些处理; 也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。
WEB开发人员通过Filter技术,可以对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、计算系统的响应时间等一些高级功能。

Filter 过滤器

在 Servlet 规范中,有一个 javax.servlet.Filter 接口。实现了该接口的类称为过滤器,接口中有三个方法可以重写:

  • init():初始化方法,即 Filter 被创建后,在后面用到的资源的初始化工作,可以在这里完成。
  • doFilter():Filter 的核心方法,对于请求与响应的过滤,就是在这个方法中完成的。
  • destroy():销毁方法。 Filter 被销毁前所调用执行的方法。对于资源的释放工作,可以在这里完成。

创建Filter过滤器的步骤

1.创建一个类实现javax.servlet.Filter接口
2.重写接口中的方法
3.在web.xml文件中配置Filter

创建一个类实现Filter接口并重写接口中的方法

package com.monkey1024.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter implements Filter{

    public MyFilter(){
        System.out.println("实例化");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("过滤请求");
        //通过过滤器继续访问资源
        chain.doFilter(request, response);
        System.out.println("过滤响应");
    }

    @Override
    public void destroy() {
        System.out.println("销毁");
    }



}

注意:在Filter的doFilter方法内如果没有执行doFilter(request, response)方法,那么服务器中的资源是不会被访问到的。

在web.xml中配置filter,跟配置servlet类似:

 <filter>
      <filter-name>my</filter-name>
      <filter-class>com.monkey1024.filter.MyFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>my</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>


表示全路径匹配,Filter的全路径匹配只支持/*,不支持/

将项目部署到tomcat并启动成功之后,会在控制台中看到下面信息:
实例化
初始化

访问项目,会在控制台中看到下面信息:
过滤请求
过滤响应

正常关闭tomcat,会在控制台中看到下面信息:
销毁

通过上面的操作,可以得出Filter的生命周期如下:

  • 当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
  • 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
  • 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.

一个Filter的生命周期跟servlet有些类似,需要经历实例化—>初始化—>doFilter—>销毁四个过程。

dispatcher 标签

在 filter-mapping 中还有一个子标签 dispatcher ,用于设置过滤器所过滤的请求类型。
其有五种取值:REQUEST、FORWARD、INCLUDE、ASYNC、ERROR,默认是REQUEST

  • FORWARD
    若请求是由一个 Servlet 通过 RequestDispatcher 的 forward()方法所转发的, 那么这个请求将被值为 FORWARD 的 Filter 拦截。即当前 Filter 只会拦截由RequestDispatcher 的 forward()方法所转发的请求。其它请求均不拦截。
  • INCLUDE
    只要是通过<jsp:include page=”xxx.jsp” />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器。
  • ERROR
    在 web.xml 中可以配置错误页面 error-page ,当发生指定状态码的错误后,会跳转到指定的页面。而这个跳转同样是发出的请求。若的值设置为 EEROR,则当前过滤器只会拦截转向错误页面的请求,其它请求不会拦截。

    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>
    
  • ASYNC

会拦截AsyncContext对象发出的请求

  • REQUEST
    默认值。即不设置 dispatcher 标签,也相当于指定了其值为 REQUEST。只要请求不是由 RequestDispatcher 的 forward()方法或 include()方法转发的,那么该 Filter均会被拦截,即使是向错误页面的跳转请求,同样会被拦截。

Filter特征

  1. Filter 是单例多线程的。
  2. Filter 是在应用被加载时创建并初始化, 这是与 Servlet 不同的地方。 Servlet 是在该 Servlet被第一次访问时创建。 Filter 与 Servlet 的共同点是,其无参构造器与 init()方法只会执行一次。
  3. 用户每提交一次该 Filter 可以过滤的请求,服务器就会执行一次 doFilter()方法,即doFilter()方法是可以被多次执行的。
  4. 当应用被停止时执行 destroy()方法,Filter 被销毁,即 destroy()方法只会执行一次。
  5. 由于 Filter 是单例多线程的,所以为了保证其线程安全性,一般情况下是不为 Filter 类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题。

FilterConfig

在Filter中的init方法上有一个参数叫FilterConfig,这是Filter的配置对象,通过FilterConfig对象可以获取当前 Filter 在 web.xml中的配置信息,这与ServletConfig类似。一个 Filter 对象一个 FilterConfig 对象,多个 Filter 对象会有多个 FilterConfig 对象。它的作用:

  • 获取Filter的名称
  • 获取初始化参数
  • 获取ServletContext对象
    FilterConfig 接口中的方法与 ServletConfig 接口中的方法,方法名与意义完全相同。
package com.monkey1024.filter;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * FilterConfig
 */
public class ConfigFilter implements Filter {
    FilterConfig fConfig;

    public void init(FilterConfig fConfig) throws ServletException {
        this.fConfig = fConfig;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //获取ServletContext
        ServletContext sc = fConfig.getServletContext();
        System.out.println("ServletContext:" + sc);

        //获取FilterName
        String filterName = fConfig.getFilterName();
        System.out.println("filter name:" + filterName);

        //获取指定的初始化参数值
        String param = fConfig.getInitParameter("name");
        System.out.println(param);

        //获取所有的初始化参数名称
        Enumeration<String> names = fConfig.getInitParameterNames();
        while(names.hasMoreElements()){
            String name = names.nextElement();
            String value = fConfig.getInitParameter(name);
            System.out.println(name + ":" + value);
        }

        //转向下个资源
        chain.doFilter(request, response);
    }

    public void destroy() {

    }



}

在web.xml文件中注册Filter:

<filter>
    <filter-name>ConfigFilter</filter-name>
    <filter-class>com.monkey1024.filter.ConfigFilter</filter-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>monkey1024</param-value>
    </init-param>
    <init-param>
        <param-name>password</param-name>
        <param-value>123456</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>ConfigFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

多个Filter的执行过程

若web应用中配置了多个 Filter,那么这些 Filter 的执行过程是以“链”的方式执行的。即会将这
些与请求相匹配的 Filter 串成一个可执行的“链”,然后按照这个链中的顺序依次执行。这些 Filter 在链中的顺序与它们在 web.xml 中的注册顺序相同,即 web.xml 中的注册顺序就是 Filter 的执行顺序。
一个 Filter 的执行完毕,转而执行另一个 Filter,这个转向工作是由 FilterChain 的 doFilter()方法完成的。当然,若当前 Filter 是最后一个 Filter,则 FilterChain 的 doFilter()会自动转向最终的请求资源。
当请求到达 Filter 后,Filter 可以拦截到请求对象,并对请求进行修改。修改过后,再将
该修改过的请求转向下一个资源。
当最终的资源执行完毕,并形成响应对象后,会按照请求访问 Filter 的倒序,再次访问Filter。此时 Filter 可以拦截到响应对象,并对响应进行修改。最终,客户端可以收到已被修改过的响应。

多个Filter的执行过程