1、Filter基础:

-  Filter是Servlet规范的三大组件之一。顾名思义,就是过滤。可以在请求到达目标资源之前先对请求进行拦截过滤,即对请求进行一些处理;也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。

2、Filter的生命周期:

a、Filter的生命周期与Servlet的生命周期类似,其主要生命周期阶段有四个:Filter对象的创建、Filter对象的初始化、Filter执行doFilter()方法以及最终Filter对象被销毁。

-   Filter的整个生命周期过程的执行,均由Web服务器负责管理。即Filter从创建到销毁的整个过程中方法的调用,都是由Web服务器负责调用执行的,程序员无法控制其执行流程。

-  过滤器生命周期示例:

-  自定义的Filter类需要实现Filter接口(javax.servlet.Filter),并实现其接口中定义的方法:

package com.geeklicreed.filters;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 SomeFilter implements Filter {    public SomeFilter() {        System.out.println("SomeFilter被创建");    }    @Override    public void init(FilterConfig filterConfig) throws ServletException {        System.out.println("SomeFilter被初始化");    }    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {        // 请求拦截过滤时可以编写的代码部分        System.out.println("执行SomeFilter -- before --");        chain.doFilter(request, response);        // 响应拦截过滤时可以编写的代码部分        System.out.println("执行SomeFilter -- after --");    }    @Override    public void destroy() {        System.out.println("SomeFilter被销毁");    }}

-  自定义Servlet类:

package com.geeklicreed.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SomeServlet extends HttpServlet {    private static final long serialVersionUID = 2884109743938532642L;    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        System.out.println("执行SomeServlet");    }}

-  需要注意的是,需要在web.xml中注册filter:

    
    
        
some-Filter
        
com.geeklicreed.filters.SomeFilter
    
    
        
some-Filter
        
/*
    
    
        
some-servlet
        
com.geeklicreed.servlet.SomeServlet
    
    
        
some-servlet
        
/some
    

-  启动服务器,访问项目中的/some路径资源,在控制台中显示的结果为其生命周期:

-  上例中的接口以及方法的解释:

-  javax.servlet.Filter接口:过滤器是用于过滤请求资源(servlet、静态资源)或者是从资源响应客户端的过滤任务:(或者是两者兼而有之)

-  其接口中三个定义方法:

- javax.servlet.FilterChain接口:FilterChain接口类对象是被用于servlet容器提供给开发者从视图到资源过滤请求的调用链的对象。过滤器使用过滤器链来调用链中的下一个过滤器,如果正在调用的过滤器是链中的最后一个,则会直接调用链末尾端的资源。

-  该接口中只定义一个方法:调用过滤器链中的下一个过滤器,或者如果正在调用的过滤器是链中的最后一个,则会直接调用链末尾端的资源。

b、Filter的特征:

-  Filter是单例多线程的。

-  Filter是在应用被加载时创建并初始化的,这是与Servlet不同的地方。Servlet是在该Servlet被第一次访问时创建的。Filter与Servlet的共同点是,其无参构造器与init()方法只会执行一次。

-  用户每提交一次该Filter可以过滤的请求,服务器就会执行一次doFilter()方法,即doFilter()方法可以被多次执行。

-  当应用被停止时执行destroy()方法,Filter被销毁,即destroy()方法只会执行一次。

-  由于Filter是单例多线程的,所以为了保证其线程安全性,一般情况下是不为Filter类定义可修改的成员变量。因为每个线程均可修改这个成员变量,会出现线程安全问题。

3、javax.servlet.FilterConfig接口:通常指代filter在web.xml中的注册信息:

-  FilterConfig接口中的方法可以获取关于当前Filter在web.xml中的信息:(ServletContext、filterName、initParameter等):

-  FilterConfig接口中的方法在filter类中使用的示例:

-  可以在自定义Filter类中定义私有化的成员变量(并提供getFilterConfig()方法),在init(FilterConfig filterConfig)方法接收。在doFilter方法中使用这个filterConfig成员变量:

package com.geeklicreed.filters;import java.io.IOException;import java.util.Enumeration;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 SomeFilter implements Filter {    private FilterConfig filterConfig;    @Override    public void init(FilterConfig filterConfig) throws ServletException {        this.filterConfig = filterConfig;    }    public FilterConfig getFilterConfig() {        return filterConfig;    }    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {        System.out.println(filterConfig.getFilterName());        System.out.println(filterConfig.getServletContext());        System.out.println(filterConfig.getInitParameter("username"));        Enumeration
 initParameterNames = filterConfig                .getInitParameterNames();        while (initParameterNames.hasMoreElements()) {            String initParameterName = initParameterNames.nextElement();            System.out.println(initParameterName + " --> "                    + filterConfig.getInitParameter(initParameterName));        }        chain.doFilter(request, response);    }    @Override    public void destroy() {    }}

-  需要注意的是,在web.xml中的<filter>标签中,可以提供<init-param>标签,以提供初始化参数:

    
    
        
Some-Filter
        
com.geeklicreed.filters.SomeFilter
        
            
username
            
geeklicreed
        
        
            
age
            
21
        
    
    
        
Some-Filter
        
/*
    

-  当访问一个index.jsp页面时,控制台中的打印如下:

4、<dispatcher>标签的四种取值:

-  在<filter-mapping>中还有一个子标签<dispatcher>,用于设置过滤器的请求类型。其有四种取值,REQUEST、FORWARD、INCLUDE、ERROR。

-  FORWARD:若请求是由一个Servlet通过RequestDsipatcher的forward()方法所转发的,那么这个请求将被<dispatcher>值为FORWARD的Filter所拦截。即当前Filter只会拦截由RequestDispatcher的forward()方法所转发的请求。其他请求均

不拦截。

-  INCLUDE:当前Fillter只会拦截由RequestDsipatcher的include()方法所转发的请求。其他请求均不拦截。

-  REQUEST:表示当前过滤其只会拦截普通请求,但是对forward和include的跳转不进行拦截。(默认值)

-  ERROR:表示当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截。

-  <dispatcher>标签的ERROR取值示例:

-  在web.xml中填写如下代码,使得当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截。

    
    
        
Some-Filter
        
com.geeklicreed.filters.SomeFilter
    
    
        
Some-Filter
        
/*
        
ERROR
    
    
    
        
404
        
/error.jsp
    

5、多个Filter的执行顺序问题:

-  当应用中存在多个Filter时,其执行顺序与注册顺序一致。(如上图所示,先执行OneFilter的请求过滤拦截,再是TwoFilter的请求过滤拦截,后是SomeServlet的执行,后是TwoFilter的响应过滤拦截,最后是TwoFilter的请求过滤拦截)

6、Filter的执行原理:

-  当某资源的请求到达Web容器时,会先对请求进行解析,使用解析出来的URI作为比较对象,从Map(Map的key为<url-pattern>的值,value为Filter实例对象的引用)中查找是否存在相匹配的key。若存在,那么读取其value,即Filter对象的引用,将该引用存入到数组中。然后继续向后查找,直到将该Map查找完毕。这样在数组中就存在按照查找顺序排好序的Filter引用。

-  数组初始化完毕后,开始按照数组元素顺序进行执行。所以数组中的Filter全部执行完毕之后,再跳转到请求的目标资源。(数组中存放着与请求相匹配的所有Filter)

7、附加说明:

a、若Filter为全路径匹配方式,那么url-pattern只能写为/*,而不能够写为/。(写为/,无论是静态资源,还是动态资源都不起作用)

-  <filter-mapping>标签中可以不使用<url-pattern>,但需要指定<servlet-name>,即当前过滤器拦截的是对指定Servlet的请求。

b、filter过滤器的应用:

-  可以在自定义的过滤器(MyCharactorEncodingFilter类)中解决POST提交中文乱码问题。

package com.geeklicreed.filters;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 MyCharactorEncodingFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {        //解决请求参数中文乱码问题        request.setCharacterEncoding("UTF-8");        //解决响应中文乱码问题        response.setContentType("text/html;charset=UTF-8");        chain.doFilter(request, response);    }    @Override    public void destroy() {    }}