Filter是从Servlet2.3规范开始新增的功能,并在Servlet2.4规范中得到增强,接下来让我们一起来看看Filter的真实面目吧。
一. 概念
过滤器就是在源数据和目的数据之间起过滤作用的中间组件。对Web应用来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和资源之间的请求与响应信息,并对这些信息进行过滤。
二. 执行流程
当Web容器接受到一个对资源的请求时,它就会判断是否有过滤器与这个资源相关联(这是一个自动的过程)。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时候,容器同样会将响应先转发给过滤器,再过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端.。
如果有多个过滤器,则它会像一个链(根据web.xml中的位置)一样执行。
我们先看一个图:
三. 实例及解析
我们来看一个登录的例子,它需要用到两个过滤器,一个是转换编码格式,一个是判断是否登录。
先看一下这个例子的时序图:
接下来我们看一下源码:
login.jsp
[java] <pre class="java" name="code"><%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%
String command = request.getParameter("command");
if ("login".equals(command)) {
if ("dan".equals(request.getParameter("userId"))
&& "123".equals(request.getParameter("password"))) {
//登陆成功将用户信息放到session中
session.setAttribute("user_name",
request.getParameter("userId"));
//设置超时,单位:秒
session.setMaxInactiveInterval(6000);
//重定向到主控页面
response.sendRedirect(request.getContextPath() + "/main.jsp");
}
}
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>登录</title>
<SCRIPT language=JavaScript>
function init() {
loginForm.userId.focus();
}
</SCRIPT>
</head>
<body οnlοad=init()>
<FORM name="loginForm">
<input type="hidden" name="command" value="login">
用户名:
<INPUT name="userId" value="dan" type="text" size="20" maxlength="20">
密 码:
<INPUT name="password" value="123" type="password" size="21" maxlength="20">
<input type="submit" οnclick="submitForm()" value="提交" name="login" id="login">
</FORM>
</body>
</html>
<pre class="java" name="code"><%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%
String command = request.getParameter("command");
if ("login".equals(command)) {
if ("dan".equals(request.getParameter("userId"))
&& "123".equals(request.getParameter("password"))) {
//登陆成功将用户信息放到session中
session.setAttribute("user_name",
request.getParameter("userId"));
//设置超时,单位:秒
session.setMaxInactiveInterval(6000);
//重定向到主控页面
response.sendRedirect(request.getContextPath() + "/main.jsp");
}
}
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>登录</title>
<SCRIPT language=JavaScript>
function init() {
loginForm.userId.focus();
}
</SCRIPT>
</head>
<body οnlοad=init()>
<FORM name="loginForm">
<input type="hidden" name="command" value="login">
用户名:
<INPUT name="userId" value="dan" type="text" size="20" maxlength="20">
密 码:
<INPUT name="password" value="123" type="password" size="21" maxlength="20">
<input type="submit" οnclick="submitForm()" value="提交" name="login" id="login">
</FORM>
</body>
</html>
CharsetEncodingFilter.java
[java] package filter;
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 CharsetEncodingFilter implements Filter {
private String encoding;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//设置字符集
servletRequest.setCharacterEncoding(encoding);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//取得初始化参数
this.encoding = filterConfig.getInitParameter("encoding");
}
}
package filter;
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 CharsetEncodingFilter implements Filter {
private String encoding;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//设置字符集
servletRequest.setCharacterEncoding(encoding);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//取得初始化参数
this.encoding = filterConfig.getInitParameter("encoding");
}
}
AuthFilter.java
[java] package filter;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class AuthFilter implements Filter {
public void destroy() { }
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestURI = req.getRequestURI().substring(
req.getRequestURI().indexOf("/", 1),
req.getRequestURI().length());
if (!"/login.jsp".equals(requestURI)) {
HttpSession session = req.getSession(false);
if (session == null || session.getAttribute("user_name") == null) {
res.sendRedirect(req.getContextPath() + "/login.jsp");
return;
}
}
// 继续访问其他资源
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) throws ServletException {}
}
package filter;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class AuthFilter implements Filter {
public void destroy() { }
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestURI = req.getRequestURI().substring(
req.getRequestURI().indexOf("/", 1),
req.getRequestURI().length());
if (!"/login.jsp".equals(requestURI)) {
HttpSession session = req.getSession(false);
if (session == null || session.getAttribute("user_name") == null) {
res.sendRedirect(req.getContextPath() + "/login.jsp");
return;
}
}
// 继续访问其他资源
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) throws ServletException {}
}
Web.xml
[html] <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<filter>
<filter-name>CharsetEncodingFilter</filter-name>
<filter-class>filter.CharsetEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB18030</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharsetEncodingFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharsetEncodingFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<filter>
<filter-name>CharsetEncodingFilter</filter-name>
<filter-class>filter.CharsetEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB18030</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharsetEncodingFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharsetEncodingFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
</web-app>
Filter中主要有三个方法init(),doFilter(),destroy()
1.init(FilterConfigfilterConfig)
Web容器调用该方法该方法来初始化过滤器。容器在调用该方法时,向过滤器传递
FilterConfig对象,利用FilterConfig对象可以得到ServletContext对象,以及部署描述符中配置的过滤器的初始化参数。
从图中可以看出来Listener,Filter,Servlet及Tomcat的一个启动顺序。
2.doFilter(ServletRequestrequest, ServletResponse response,FilterChain chain)
这是Filter的主要方法,其中的request和response和servlet里的参数一样,至于chain是用于后面对请求的转发的,该参数的chain.doFilter方法是一种回调过程。
该方法以chain.doFilter()为界限,先执行前面的代码,然后执行chain.doFilter()方法来将请求传递给下一个Filter(如果没有Filter了就传递给Servlet),等到这条链执行完后,再往回执行后面的代码,它是使用回调来实现的。
大家可以通过下面的图来进行理解:
3.destroy()
这个方法servlet的destroy()方法一样,都是当服务器断开的时候才执行该销毁方法,Filter也是实例化一次,多次调用。
=============================================================
java filter里<filter-mapping>中的<dispatcher>作用
2.4版本的servlet规范在部属描述符中新增加了一个<dispatcher>元素,这个元素有四个可能的值:即 REQUEST、FORWARD、INCLUDE和ERROR。可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>,使得filter将会作用于直接从客户端过来的request、通过forward过来的request、通过 include过来的request和通过<error-page>过来的request。如果没有指定任何< dispatcher >元素,默认值是REQUEST。可以通过下面几个例子来辅助理解。
例1:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
</filter-mapping>
这种情况下,过滤器将会作用于直接从客户端发过来的以/products/…开始的请求。因为这里没有制定任何的< dispatcher >元素,默认值是REQUEST。
例2:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<servlet-name>ProductServlet</servlet-name>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
这种情况下,如果请求是通过request dispatcher的include方法传递过来的对ProductServlet的请求,则要经过这个过滤器的过滤。其它的诸如从客户端直接过来的对ProductServlet的请求等都不需要经过这个过滤器。
指定filter的匹配方式有两种方法:直接指定url-pattern和指定servlet,后者相当于把指定的servlet对应的url-pattern作为filter的匹配模式。
filter的路径匹配和servlet是一样的,都遵循servlet规范中《SRV.11.2 Specification of Mappings》一节的说明
例3:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
在这种情况下,如果请求是以/products/…开头的,并且是通过request dispatcher的forward方法传递过来或者直接从客户端传递过来的,则必须经过这个过滤器。