循序渐进写一个 Servlet (5) - Filter
Servlet(Server Applet),全称 Java Servlet,是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。本系列将一步步地写出一个 Servlet 程序。
这篇博文将演示如何创建和使用 filter。
什么是 Filter
当客户端向 servlet 容器发送请求时,请求通常会直接发送到 servlet 进行处理,就像下图这样:
但是,如果希望在请求被 servlet 处理之前和之后,再进行一些附加的处理,就可以使用 Filter
完成。
一个常见的使用场景是,在 filter 中定义如何检查请求是否合法,比如请求头中是否携带了有效的认证和鉴权信息;或者可以在 filter 中针对请求和响应记录日志。
怎么使用 Filter
javax.servlet.Filter
接口定义了一个 filter 的生命周期,要创建一个 filter,就要实现 Filter
接口。
Filter
接口包含下列方法声明:
init()
,用于定义在初始化这个 filter 时要执行的操作,该方法在 filter 的生命周期内只会执行一次;doFilter()
,用于定义这个 filter 要进行的操作,每当有请求被发送到与该 filter 绑定的资源时,该方法都会被执行一次;destroy()
,用于定义在停止这个 filter 时要执行的操作,只会在一个 filter 被销毁时执行。
创建一个实现 Filter
接口的类
1 | public class Filter1 implements Filter { |
定义这个 filter 的行为
在 doFilter()
方法内定义这个 filter 的行为。
1 |
|
在容器中注册 filter
与 servlet 一样,filter 也需要在容器中注册之后才能发挥作用。注册 filter 也有两种方式:通过 web.xml
,或者通过 @WebFilter
注解。
这里有一点需要注意,虽然 filter 之间没有依赖关系,但是如果要保证 filter 的执行顺序,那么必须使用 web.xml
来注册。
Servlet 3.0 规范的 8.2.3
节中有如下说明:
If the order in which the listeners, servlets, filters are invoked is important to an application then a deployment descriptor must be used.
因为使用注解注册的 filter,其调用顺序没有在规范中指定。
As described above, when using annotations to define the listeners, servlets and filters, the order in which they are invoked is unspecified.
如果一定要使用注解并保证 filter 的执行顺序,那么可以参考 Stack Overflow 中这篇回答。
为了演示 filter 的执行顺序,这里再增加一个名为 Filter2
的 filter,内容与 Filter1
类似。
web.xml
在 web.xml
中增加如下配置:
1 | <filter> |
filter
标签描述了一个 filter 的基本信息,其中 filter 名称 (filter-name
) 和 filter 所在类 (filter-class
) 为必填项。
filter-mapping
标签描述了一个 filter 将与哪个 URL 或者与哪个 servlet 绑定,filter-name
指定使用哪个 filter 处理请求,url-pattern
指定发往哪个 URL 的请求会触发这个 filter,servlet-name
指定发往哪个 servlet 的请求会触发这个 filter。url-pattern
和 servlet-name
可以同时存在,也可以同时存在多个。
filter-mapping
标签的先后顺序,将决定 filter 链中各个 filter 被调用的先后顺序。如上文中先配置了 filter1
后配置了 filter2
,那么在请求到达时,会先执行 filter1
然后再执行 filter2
。
配置完毕后部署并运行该项目,向 http://localhost:8080/servletdemo/DemoServlet
发送一个请求,在控制台可以看到如下输出:
1 | Request passing through Filter 1 |
@WebFilter
注解
@WebFilter
是 Servlet 3.0
中新增的特性,在 Tomcat 7
及以前版本中将无法工作。
以 Filter1
为例,为其添加如下注解:
1 |
filter-name
属性指定了这个 filter 的名称。
有三个属性可以指定 filter 的触发条件:
value
urlPatterns
servletNames
以上三个属性都可以接受一个字符串,或者用大括号包括起来的多个字符串。
在注解只有一个参数,并且该参数是指定要匹配的 URL 时,建议使用 value
属性,比如这样:
1 | // value为默认的属性 |
否则,建议使用 urlPatterns
属性和 servletNames
属性。不允许 value
和 urlPatterns
同时出现。
Servlet 3.0 规范的 8.1.2 @WebFilter
节中说明原文如下:
It is recommended to use value when the only attribute on the annotation is the url pattern and to use the urlPatterns attribute when the other attributes are also used. It is illegal to have both value and urlPatterns attribute used together on the same annotation.
配置完毕后部署并运行该项目,向 http://localhost:8080/servletdemo/DemoServlet
发送一个请求,在控制台可以看到如下输出:
1 | Request passing through Filter 1 |