Servlet 3.1 开发过滤器( Filter )
Java Servlet 过滤器(Filter) 可以动态地拦截请求和响应,以改变或使用包含在请求或响应中的信息
Java Servlet 过滤器(Filter) 是一个实现了 javax.servlet.Filter 接口的 Java 类。
Java Servlet 允许将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet
Java Servlet 过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面
Servlet 容器会在调用 Servlet 前调用所有附加的 Servlet 过滤器
Servlet 过滤器可以实现以下目的:
- 在客户端的请求访问后端资源之前,拦截这些请求
- 在服务器的响应发送回客户端之前,处理这些响应
Servlet 规范建议的各种类型的过滤器有
过滤器 |
---|
身份验证过滤器(Authentication Filters) |
数据压缩过滤器(Data compression Filters) |
加密过滤器(Encryption Filters) |
触发资源访问事件过滤器 |
图像转换过滤器(Image Conversion Filters) |
日志记录和审核过滤器(Logging and Auditing Filters) |
MIME-TYPE 链过滤器(MIME-TYPE Chain Filters) |
标记化过滤器(Tokenizing Filters) |
XSL/T 过滤器(XSL/T Filters),转换 XML 内容 |
过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到应用程序的部署描述符中的 Servlet 名称或 URL 模式
当 Web 容器启动 Web 应用程序时,它会为 web.xml 中声明的每一个过滤器创建一个实例
Filter 的执行顺序与在 web.xml 配置文件中的配置顺序一致
一般把 Filter 配置在所有的 servlet 之前
Servlet 3.0
Servlet 3.0 以上的版本可以通过 注解 方式添加,而无需配置 web.xml
@WebFilter 的属性
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定过滤器的 name 属性,等价于 <filter-name> |
value | String[] | 该属性等价于 urlPatterns 属性。但是两者不应该同时使用 |
urlPatterns | String[] | 指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签 |
servletNames | String[] | 指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 <servlet-name> 的取值 |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式。具体取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
initParams | WebInitParam[] | 指定一组过滤器初始化参数,等价于 <init-param> 标签 |
asyncSupported | boolean | 声明过滤器是否支持异步操作模式,等价于 <async-supported> 标签 |
description | String | 该过滤器的描述信息,等价于 <description> 标签 |
displayName | String | 该过滤器的显示名,通常配合工具使用,等价于 <display-name> 标签 |
Servlet 过滤器 (Filter) 方法
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类
javax.servlet.Filter 接口定义了三个方法:
方法 & 描述 |
---|
public void doFilter (ServletRequest, ServletResponse, FilterChain) 完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的 doFilter 方法。FilterChain 用于访问后续过滤器 |
public void init( FilterConfig filterConfig ) web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次) 开发人员通过init方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象 |
public void destroy() Servlet 容器在销毁过滤器实例前调用该方法,在该方法中释放 Servlet 过滤器占用的资源 |
FilterConfig 使用
Filter 的 init 方法中提供了一个 FilterConfig 对象
web.xml 文件文件过滤器部分
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>LogFilter</filter-name> <filter-class>cn.twle.demo.LogFilter</filter-class> <init-param> <param-name>site</param-name> <param-value>www.twle.cn</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/servlet/filter/*</url-pattern> </filter-mapping> </web-app>
在 init 方法使用 FilterConfig 对象获取参数
public void init(FilterConfig config) throws ServletException { // 获取初始化参数 String site = config.getInitParameter("site"); // 输出初始化参数 System.out.println("网站域名: " + site); }
范例
下面的范例演示了 Server 过滤器的使用,它会输出网站名称和地址
cn/twle/demo/LogFilter.java
// author: 简单教程(www.twle.cn) // Copyright © 2015-2065 www.twle.cn. All rights reserved. package cn.twle.demo; import javax.servlet.*; import java.util.*; import javax.servlet.annotation.*; @WebFilter(filterName="LogFilter",urlPatterns="/filter/*" ,initParams= {@WebInitParam(name="site", value="www.twle.cn"),}) public class LogFilter implements Filter { public void init(FilterConfig config) throws ServletException { // 获取初始化参数 String site = config.getInitParameter("site"); // 输出初始化参数 System.out.println("网站域名: " + site); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException { // 输出站点名称 System.out.println("网站网址:http://www.twle.cn"); // 把请求传回过滤链 chain.doFilter(request,response); } public void destroy( ){ /* 在 Filter 实例被 Web 容器从服务移除之前调用 */ } }
cn/twle/demo/FilterDisplayHeader.java
// author: 简单教程(www.twle.cn) // Copyright © 2015-2065 www.twle.cn. All rights reserved. package cn.twle.demo; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.WebServlet; import java.util.Enumeration; @WebServlet(name = "FilterDisplayHeaderServlet", urlPatterns = {"filter/display_header"}) public class FilterDisplayHeaderServlet extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String title = "HTTP Header 请求实例 | 简单教程(www.twle.cn)"; String docType = "<!DOCTYPE html>"; out.println(docType + "<title>" + title + "</title>"+ "<body bgcolor=\"#f0f0f0\">\n" + "<p>" + title + "</p>\n" + "<table border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + "<th>Header 名称</th><th>Header 值</th>\n</tr>"); Enumeration headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String paramName = (String)headerNames.nextElement(); out.print("<tr><td>" + paramName + "</td>"); String paramValue = request.getHeader(paramName); out.println("<td> " + paramValue + "</td></tr>"); } out.println("</table></body>"); } }
Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping)
如果你使用的不是注解的方式,那么需要配置 web.xml 添加过滤器
注解和 web.xml 配置最好不要同时有,不然会出现一大堆莫名其妙的问题
定义过滤器,然后映射到一个 URL 或 Servlet,这与定义 Servlet,然后映射到一个 URL 模式方式大致相同
在部署描述符文件 web.xml 中为 filter 标签创建下面的条目
<?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>LogFilter</filter-name> <filter-class>cn.twle.demo.LogFilter</filter-class> <init-param> <param-name>site</param-name> <param-value>www.twle.cn</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/filter/*</url-pattern> </filter-mapping> <servlet> <!-- 类名 --> <servlet-name>FilterDisplayHeaderServlet</servlet-name> <!-- 所在的包 --> <servlet-class>cn.twle.demo.FilterDisplayHeaderServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FilterDisplayHeaderServlet</servlet-name> <!-- 访问的网址 --> <url-pattern>/filter/display_header</url-pattern> </servlet-mapping> </web-app>
上述过滤器适用于 /filter/ 下的所有 Servlet,因为我们在配置中指定 /filter/**
在浏览器上输入 http://localhost:8080/servlet/filter/display_header 显示结果如下
然后控制台输出如下内容
使用多个过滤器
Web 应用程序可以根据特定的目的定义若干个不同的过滤器
假设我们定义了两个过滤器 AuthenFilter 和 LogFilter
那么需要创建一个如下所述的不同的映射,其余的处理与上述所讲解的大致相同
<filter> <filter-name>LogFilter</filter-name> <filter-class>cn.twle.demo.LogFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter> <filter-name>AuthenFilter</filter-name> <filter-class>cn.twle.demo.AuthenFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/filter/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器的应用顺序
web.xml 上的顺序
web.xml 中的 filter-mapping 元素的顺序决定了 Web 容器应用过滤器到 Servlet 的顺序
若要反转过滤器的顺序,则只需要在 web.xml 文件中反转 filter-mapping 元素即可
例如,上面的 web.xml 将先应用 LogFilter,然后再应用 AuthenFilter,但是下面的实例将颠倒这个顺序:
<filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/filter/*</url-pattern> </filter-mapping>
@WebFilter 中的顺序
使用 @WebFilter 注解方式没有可以设置顺序的地方,Servlet 根据类名的字母排序决定谁先调用
所以以上两个 Filter 的调用顺序是: 先 AuthenFilter 后 LogFilter
如果需要反过来,可以将两个过滤器分别命名为 Filter0_LogFilter 和 Filter1_AuthenFilter
web.xml 配置各节点说明
-
- <filter>
- 指定一个过滤器
-
- <filter-name>
- 用于为过滤器指定一个名字,该元素的内容不能为空。
-
- <filter-class>
- 元素用于指定过滤器的完整的限定类名
-
- <init-param>
- 元素用于为过滤器指定初始化参数
它的子元素
- <param-name> 指定参数的名字
- <param-value> 指定参数的值
在过滤器中,可以使用
FilterConfig
接口对象来访问初始化参数。 -
- <filter-mapping>
- 元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径- <filter-name> 子元素用于设置filter的注册名称。该值必须是在 <filter> 元素中声明过的过滤器的名字
-
- <url-pattern>
- 设置 filter 所拦截的请求路径(过滤器关联的URL样式)
-
- <servlet-name>
- 指定过滤器所拦截的Servlet名称
-
- <dispatcher>
- 指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是
REQUEST
,INCLUDE
,FORWARD
和ERROR
之一,默认REQUEST
用户可以设置多个 <dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
<dispatcher> 子元素可以设置的值及其意义
REQUEST
:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。INCLUDE
:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。FORWARD
:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。ERROR
:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。