Fliter组件

Servlet规范中的Filter过滤器组件可以对用户的请求进行预处理和后处理,简而言之就是可以拦截发给Servlet的请求(ServletRequest)和发给客户端的响应(ServletResponse),Filter本身还实现了责任链设计模式,多个Filter还可以形成处理链,共同构成一些复杂的业务逻辑。Filter组件可用于认证鉴权、记录访问日志、统一处理编解码等功能。

创建和配置Filter组件

Filter组件和Servlet类似,也可以通过注解或XML的方式声明。Filter类需要实现Filter接口,该接口需要实现doFilter()方法。下面是一个过滤器类的例子。例子中,在请求传递给Servlet之前和之后会分别打印一条信息。

DemoFilter.java

package com.gacfox.demo.demoweb.filter;

import javax.servlet.*;
import java.io.IOException;

public class DemoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("请求处理前");
        chain.doFilter(request, response);
        System.out.println("请求处理后");
    }
}

其中,chain.doFilter(request, response);表示将请求交给下一级组件处理,我们在该行代码之前和之后执行的逻辑其实就是请求拦截和响应拦截了。

通过XML注册Filter

使用XML注册Filter是最基础的方法,下面是一个配置例子。

<filter>
    <filter-name>DemoFilter</filter-name>
    <filter-class>com.gacfox.demo.demoweb.filter.DemoFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>DemoFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

配置中,我们指定了Filter的名字,类全名,以及拦截的路径。注意Filter过滤器的路径配置经常使用通配符来拦截多个路径,这里特别介绍一下<url-pattern>属性中通配符*的使用。假设项目的ContextPath是/demoweb,下面是一些常见用法:

  • /aaa:只匹配对URL为/demoweb/aaa的请求
  • /* :匹配所有对该项目的请求
  • *.do:匹配对该项目所有带有.do后缀的请求,例如/demoweb/a.do/demoweb/aaa/a.do

我们可以发现,其实Filter组件匹配请求的方式分为两类,路径匹配和后缀匹配。上面前两种是路径匹配,后一种是后缀匹配。

另外注意,以下写法都是错误的:

  • /:匹配不到任何请求
  • /*.do:错误写法,启动直接报错

不过有时候我们对于URL的匹配逻辑太过特殊,<url-pattern>可能无法满足,此时我们就不得不在Filter组件的代码中对请求路径进行判断了。假设请求地址是http://localhost:8080/demoweb/a.doHttpServletRequest中有这样几个方法供我们获取请求地址:

  • request.getRequestURI():返回值是String类型/demoweb/a.do
  • request.getRequestURL():返回值是StringBuffer类型http://localhost:8080/demoweb/a.do

通过注解注册Filter

除了使用XML配置,我们也可以通过注解的方式注册Filter过滤器组件,下面是一个例子。

@WebFilter(filterName = "DemoFilter", urlPatterns = "/*")
public class DemoFilter implements Filter {
    // ... 具体业务逻辑
}

注解配置的属性和XML的完全相同的。

配置初始化参数

对于Filter组件,也有类似Servlet的<init-param>配置,使用方法完全相同,这里就不再赘述了。

Filter的生命周期

Filter组件的生命周期和Servlet相同。默认情况下,第一次收到请求后初始化,直到Web应用关闭时销毁。

package com.gacfox.demo.demoweb.filter;

import javax.servlet.*;
import java.io.IOException;

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

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("请求处理前");
        chain.doFilter(request, response);
        System.out.println("请求处理后");
    }
}

在Filter中,我们可以通过实现init()destroy()这两个方法来编写Filter初始化和销毁的生命周期回调逻辑。

Filter的执行顺序

如果应用程序注册了多个Filter,它们的执行顺序是按照在web.xml中声明的顺序执行的。也就是说,先声明的Filter会先被调用,后声明的Filter会后被调用。例如下面配置中,Filter1会在Filter2之前被调用,因为它在配置文件中先声明。

<filter>
    <filter-name>Filter1</filter-name>
    <filter-class>com.gacfox.demo.demoweb.filter.Filter1</filter-class>
</filter>
<filter-mapping>
    <filter-name>Filter1</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>Filter2</filter-name>
    <filter-class>com.gacfox.demo.demoweb.filter.Filter2</filter-class>
</filter>
<filter-mapping>
    <filter-name>Filter2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

不过如果通过@WebFilter注解注册Filter,这个顺序就很难确定了,这也是Servlet规范中的一个缺陷,不知道在未来是否会改进。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。