Hello,Web Filter

本贴最后更新于 2740 天前,其中的信息可能已经东海扬尘

同 Listener,一篇随性发挥的笔记性质的博文

定义

白话:Web Filter 是用于过滤用户请求的过滤器,请注意,它不能直接处理用户请求。
官方解释:过滤器,是一个服务器端的组件,它可以截取用户端的请求与响应信息,并对这些信息进行过滤。

用途

  • 对用户请求进行统一认证
  • 编码转换
  • 对用户发送的数据进行过滤转换
  • 转换图像格式
  • 对响应的内容进行压缩

原理

过滤器随着 web 容器的创建而创建,当有用户发送对 web 资源的请求时,过滤器会截取这些请求,经过处理后,在发送给 web 资源,而资源的响应也是先发送给过滤器,等过滤器处理完之后,再由过滤器发送给用户。

生命周期

在 web 容器初始化的时候,根据 web.xml 文件进行过滤器的实例化,所以过滤器只会实例化一次,实例化之后马上调用 init()方法,进行初始化,初始化之后,每次捕捉到用户的请求,都去执行 doFilter()方法,也就是过滤,当 web 容易关闭的时候,过滤器将会执行 destroy()方法进行销毁。

第一个 Web Filter

代码请见 jsp-basic 关于 Filter 的章节

init()

这是过滤器的初始化方法,Web 容器创建过滤器实例后将调用这个方法。这个方法可以读取 web.xml 文件中过滤器的参数。

doFilter()

这个方法完成实际的过滤操作。这个地方是过滤器的核心方法。当用户请求访问与过滤器关联的 URL 时,Web 容器将先调用过滤器的 doFilter 方法。
FilterChain 参数可以调用 chain.doFilter 方法,将请求传给下一个过滤器(或目标资源),或利用转发、重定向将请求转发到其他资源。

destroy()

Web 容器在销毁过滤器实例前调用该方法,在这个方法中可以释放过滤器占用的资源。(大多数情况下用不到)

web.xml 配置

代码如下所示:

<filter>
    <filter-name>filter的名字</filter-name>
    <filter-class>filter类的名字</filter-class>
    <init-param>//初始化的参数,可以没有,也可以有很多
        <description>可以省略的描述信息</description>
        <param-name>参数名称</param-name>
        <param-value>参数的值</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>filter的名字</filter-name>
    <url-pattern>URL</url-pattern>//当用户请求的URL和指定的URL匹配时,将触发过滤器工作
    <dispatcher>默认为REQUEST</dispatcher>//可以没有,也可以有很多,值为:REQUEST|INCLUDE|FORWORD|ERROR
</filter-mapping>

这边给的例子如下配置:

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>com.liumapp.jspbasic.filter.FirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

多过滤器的情况

多个过滤器处理同一个 web 资源

这个时候,服务器会按照 web.xml 中过滤器定义的先后顺序组装成一条链:用户请求 => 过滤器 1 => 过滤器 2 => 过滤器 3 => Web 资源。
在具体一点:

比如我们定义两个过滤器 A 和过滤器 B,A 的代码如下:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("do FilterA");
    filterChain.doFilter(servletRequest , servletResponse);
    System.out.println("filterA end");
}	

B 的代码如下:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("do FilterB");
    filterChain.doFilter(servletRequest , servletResponse);
    System.out.println("filterB end");
}

那么一个用户请求过来,首先是进入过滤器 A 的 doFilter,系统先打印出"do FilterA",随后进入 filterChain.doFilter,然后进入过滤器 B 的 doFilter,系统再打印出"do FilterB",然后执行 Web 资源的响应,然后再回到过滤器 B,系统打印出"filterB end",再回到过滤器 A,系统再打印出"filterA end",这样的一个链式操作。

过滤器的分类

即 filter-mapping 下 dispatcher 的类别(在 Servlet2.5 的情况下)。

REQUEST

用户直接访问页面时,Web 容器将会调用过滤器

INCLUDE

目标资源通过 RequestDispatcher 的 include 方法调用时,该过滤器被调用。
或者在.jsp 页面中,通过

<jsp:include page="testFilter.jsp"></jsp:include>

来访问页面,也是可以的。

FORWORD

目标资源是通过 RequestDispatcher 的 forward 访问时,该过滤器将被调用。
或者在.jsp 页面中,通过

<jsp:forward page="testFilter.jsp"></jsp:forward>

来访问页面,也是能够触发改过滤器。

ERROR

目标资源是通过申明式异常处理机制调用时,过滤器将被调用。
这里以 404 错误为栗子:
一般而言,我们可以在 web.xml 文件配置一个错误页面,当系统捕捉到 404 错误之后,直接跳转到这个页面就结束了,但如果我们使用 ERROR 类型的过滤器,那么这个错误页面在返回给用户界面时,会先经过过滤器的处理再放行。

web.xml 添加如下代码:

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

<filter>
    <filter-name>ErrorFilter</filter-name>
    <filter-class>com.liumapp.jspbasic.filter.ErrorFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ErrorFilter</filter-name>
    <url-pattern>/error.jsp</url-pattern>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

然后过滤器里面的内容就不写了,想做什么都可以,但是别忘记了要执行:

filterChain.doFilter(servletRequest , servletResponse);

REQUEST 跟 FORWARD 的区别

现在我们有一个 TestFilter,其代码如下所示:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    System.out.println("this is testFilter begin");
    HttpServletRequest req = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    response.sendRedirect(req.getContextPath() + "/testFilter.jsp");

    System.out.println("this is testFilter end");

}

对它进行配置后,我们访问 testFilter.jsp 这个脚本,毫无疑问,系统会进入一个死循环,不断的打印"this is testFilter begin"。但如果我们将代码改成:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    System.out.println("this is testFilter begin");
    HttpServletRequest req = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    //转发
    req.getRequestDispatcher("testFilter.jsp").forward(servletRequest , servletResponse);

    System.out.println("this is testFilter end");

}

然后访问 testFilter.jsp,那么页面将能够正常的显示。因为 Filter 如果是 REQUEST 类型的,那么就不会去处理 forward 转发的请求。这里呢,我们的直接跳转就相当于 request 直接请求。两者的区别就在于,重定向能够让用户看到跳转的 URL,而转发不会改变用户原来的 URL。

Servlet3.0 下的新特性

过滤器将能够支持 Async 异步处理,这有什么好处呢?

好处就是,在过滤器执行的期间,如果说是一个处理数据量很大的过程,那么执行的时间相对就会比较长,那么用户就必须等待比较长的时间才能看到响应的结果,使用异步的过滤器之后,就可以解决这个问题。

具体的操作也是通过 @WebFilter,它用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。

@WebFilter 常用属性

  • filterName :String 类型,相当于 filter-name
  • value :String[]类型,相当于 urlPatterns
  • urlPatterns :String[]类型,指定一组过滤器的 URL 匹配模式,等价于 url-pattern,注意,它跟 value 不能同时使用
  • servletNames :String[]类型,指定过滤器将应用于哪些 Servlet,取值是 @WebFilter 中的 name 属性的取值,或者是 web.xml 中 servlet-name 的值。
  • dispatcherTypes :指定过滤器的转发模式,有 ASYNC、ERROR、FORWARD、INCLUDE、REQUEST
  • initParams :WebInitParam[],指定一组过滤器初始化参数,等价于 init-param。
  • asyncSupported :boolean 类型,声明过滤器是否支持异步操作模式,等价于 async-supported
  • description :String 类型,该过滤器的描述信息,等价于 description
  • displayName :String 类型,该过滤器的显示名称,等价于 display-name,通常配合工具使用。

Async 实例

源码请看 **servlet3-jsp-basic**下面的 AsyncFilter。

注意

  • 过滤器能够改变用户请求的 Web 资源,即能够改变用户请求的路径。
  • 过滤器不是一个标准的 Servlet,所以不能够直接返回数据,即不能直接处理用户请求。
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3454 回帖 • 189 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...