Filter是从Servlet2.3规范开始新增的功能,并在Servlet2.4规范中得到增强,接下来让我们一起来看看Filter的真实面目吧。
一. 概念
过滤器就是在源数据和目的数据之间起过滤作用的中间组件。对Web应用来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和资源之间的请求与响应信息,并对这些信息进行过滤。
二. 执行流程
当Web容器接受到一个对资源的请求时,它就会判断是否有过滤器与这个资源相关联(这是一个自动的过程)。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时候,容器同样会将响应先转发给过滤器,再过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端.。
如果有多个过滤器,则它会像一个链(根据web.xml中的位置)一样执行。
我们先看一个图:
三. 实例及解析
我们来看一个登录的例子,它需要用到两个过滤器,一个是转换编码格式,一个是判断是否登录。
先看一下这个例子的时序图:
接下来我们看一下源码:
login.jsp
<%@ 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 onload=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" onclick="submitForm()" value="提交" name="login" id="login"> </FORM> </body> </html>
CharsetEncodingFilter.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"); } }AuthFilter.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 {} }
Web.xml
<?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/j2ee http://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也是实例化一次,多次调用。