【Java学习笔记】65:认识Filter(过滤器),FilterChain(过滤链)及其实现

Filter和Servlet、Listener一同作为Java web开发的三大组件。runoob中将Filter归为Servlet,实际上Filter组件和Servlet组件是两回事,但Filter接口等确实是在javax.servlet.*中,大概可以认为它也属于servlet技术。

Filter的成员方法

Filter可以对web服务器上的web资源(如JSP页面、HTML页面等)进行拦截,当客户端要访问这些资源时,可以通过建立若干个Filter来实现复杂的过滤功能,以实现如访问权限控制、访问日志等功能。
【Java学习笔记】65:认识Filter(过滤器),FilterChain(过滤链)及其实现_第1张图片
一个Filter,实际上就是一个实现了Filter接口并覆写了其三个public void方法的类。

其中init(FilterConfig xx)方法会将web服务器(唯一一次地)创建了本Filter的对象并读取到的web.xml中与这个Filter有关的配置所存至的FilterConfig类型的对象作为参数传进来,然后就会(唯一一次地)调用该方法,因此该方法是用这个参数中的配置信息来对本Filter做初始化的。开发者写如何初始化时可能会用到的本Filter的配置信息,都在这个FilterConfig类型的参数中。

doFilter(ServletRequest xx, ServletResponse xx, FilterChain xx)方法是核心的过滤操作。第一个参数是客户端传来的但还未到达要调用的Servlet的Servlet请求,在该方法中可以被修改;第二个参数是web服务器要返回给客户但还未到达客户端的Servlet响应,在该方法中可以被修改;第三个参数是过滤链,由容器实现并将其实例作为参数传进来,这个参数可以调用doFilter(Servlet请求,Servlet响应)成员方法,为已经经过本doFilter方法体处理后的Servlet请求和Servlet响应放行,即去转而执行过滤链中下一个Filter的doFilter方法。显然,只要为这个FilterChain对象参数的doFilter方法的调用设置障碍,就能实现“过滤”的功能,因为在任一Filter处被拦截,Servlet请求/响应都不会到达web服务器/客户端。

当Filter生命周期结束时web服务器会(唯一一次地)调用destroy()方法,可以在这里释放本Filter使用的资源。

web.xml中有关Filter的配置

Filter一般配置在所有的Servlet之前。

<filter>
    <filter-name>过滤器的名称filter-name>
    <filter-class>过滤器所在的包.过滤器的类名filter-class>
    <init-param>
        <param-name>初始化参数名param-name>
        <param-value>初始化参数值param-value>
    init-param>
filter>

<filter>
    ......
filter>

......

<filter-mapping>
    <filter-name>过滤器的名称filter-name>
    <url-pattern>
        写/*表示与所有资源关联,
        写xxx(资源名)表示与该资源关联,
        写Servlet名称表示与该Servlet关联
    url-pattern>
filter-mapping>

<filter-mapping>
    ......
filter-mapping>

......

使用Filter

doFilter内过滤链放行的doFilter成员方法之前做对客户端发来的Servlet请求的拦截和处理,而在放行之后做对web服务器的Servlet响应的拦截和处理。

myFilter下Filter1.java

package myFilter;

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 Filter1 implements Filter {
    @Override
    public void init(FilterConfig fc) throws ServletException {
        // 获取配置的网站名称
        String site = fc.getInitParameter("Site");
        System.out.println("过滤器初始化了,获取了网站名称是" + site);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain fc) throws IOException, ServletException {
        System.out.println("拦截客户端向服务器发的请求");
        // 把Servlet请求和回应传给过滤链上的下一过滤器
        fc.doFilter(req, resp);
        System.out.println("拦截服务器向客户端发的响应");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器要被移除了");
    }
}

配置的web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>FilterTestdisplay-name>
<filter>
    <filter-name>Filter1filter-name>
    <filter-class>myFilter.Filter1filter-class>
    <init-param>
        <param-name>Siteparam-name>
        <param-value>Filter测试param-value>
    init-param>
filter>
<filter-mapping>
    <filter-name>Filter1filter-name>
    <url-pattern>/*url-pattern>
filter-mapping>
<welcome-file-list>
    <welcome-file>go.htmlwelcome-file>
welcome-file-list>
web-app>

测试资源go.html


<html>
<head>
<meta charset="UTF-8">
<title>标题啊啊啊title>
head>
<body>
    正文啊啊啊啊啊啊啊啊啊啊
body>
html>

运行结果

开启Tomcat时可以在看到夹杂其中的信息:

......
过滤器初始化了,获取了网站名称是Filter测试
......

说明Filter的对象是web容器在初始化时创建的,并在初始化时去调用了init方法。

当用浏览器访问资源http://localhost:8080/FilterTest/go.html时,控制台又输出了:

拦截客户端向服务器发的请求
拦截服务器向客户端发的响应

当然这里并不是真的做了拦截,这说明了doFilter方法的调用时机正是在客户端请求资源,并在过滤链到达该过滤器时执行的。如果再次用浏览器访问这个资源,会继续输出这两句话。

当Tomcat关闭时,理所当然地夹杂了destroy方法中输出的信息:

......
过滤器要被移除了
......

FilterChain(过滤链)

通过修改上面的那个例子,构成两个Filter的过滤链,验证过滤器的组织顺序和web.xml中的filter-mapping标签顺序一致,验证doFilter内的过滤链的doFilter方法前后分别是对Servlet请求和Servlet响应的拦截(执行时机)。

myFilter下Filter1.java

package myFilter;

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 Filter1 implements Filter {
    @Override
    public void init(FilterConfig fc) throws ServletException {
        // 获取配置的网站名称
        String who = fc.getInitParameter("who");
        System.out.println("Filter1初始化了,获取了who参数是" + who);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain fc) throws IOException, ServletException {
        System.out.println("Filter1拦截客户端向服务器发的请求");
        // 把Servlet请求和回应传给过滤链上的下一过滤器
        fc.doFilter(req, resp);
        System.out.println("Filter1拦截服务器向客户端发的响应");
    }

    @Override
    public void destroy() {
        System.out.println("Filter1要被移除了");
    }
}

myFilter下Filter2.java

package myFilter;

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 Filter2 implements Filter {
    @Override
    public void init(FilterConfig fc) throws ServletException {
        // 获取配置的网站名称
        String who = fc.getInitParameter("who");
        System.out.println("Filter2初始化了,获取了who参数是" + who);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain fc) throws IOException, ServletException {
        System.out.println("Filter2拦截客户端向服务器发的请求");
        // 把Servlet请求和回应传给过滤链上的下一过滤器
        fc.doFilter(req, resp);
        System.out.println("Filter2拦截服务器向客户端发的响应");
    }

    @Override
    public void destroy() {
        System.out.println("Filter2要被移除了");
    }
}

配置的web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>FilterTestdisplay-name>

<filter>
    <filter-name>Filter1filter-name>
    <filter-class>myFilter.Filter1filter-class>
    <init-param>
        <param-name>whoparam-name>
        <param-value>汪汪汪汪param-value>
    init-param>
filter>

<filter>
    <filter-name>Filter2filter-name>
    <filter-class>myFilter.Filter2filter-class>
    <init-param>
        <param-name>whoparam-name>
        <param-value>喵喵喵喵param-value>
    init-param>
filter>



<filter-mapping>
    <filter-name>Filter2filter-name>
    
    <url-pattern>/go.htmlurl-pattern>
filter-mapping>

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

<welcome-file-list>
    <welcome-file>go.htmlwelcome-file>
welcome-file-list>
web-app>

运行结果

开启Tomcat时:

......
Filter1初始化了,获取了who参数是汪汪汪汪
Filter2初始化了,获取了who参数是喵喵喵喵
......

访问http://localhost:8080/FilterTest/时:

Filter2拦截客户端向服务器发的请求
Filter1拦截客户端向服务器发的请求
Filter1拦截服务器向客户端发的响应
Filter2拦截服务器向客户端发的响应

关闭Tomcat时:

......
Filter1要被移除了
Filter2要被移除了
......

非常值得注意的一点就是,过滤链的组织顺序(从客户端->服务器)是和web.xml中的标签的顺序一致,而不是标签。

你可能感兴趣的:(Java)