Filter基本工作原理:
Filter程序是一个实现了javax.servlet.Filter接口的java类,由Servlet容器进行调用和执行,需要在web.xml中进行注册和设置他所能拦截的资源。当在web.xml中注册了一个Filter来对某个Servlet程序进行拦截时,他可以对Servlet容器发送给Servlet程序的请求和Servlet程序返回给Servlet容器的响应进行拦截,可以决定是否将请求继续传递给Servlet程序以及是否对请求和相应消息进行修改。当Servlet容器开始调用某个Servlet程序时,如果发现已经注册了一个Filter程序对其进行拦截,那么Servlet容器不再直接调用Servlet程序的service方法,而是调用Filter.doFilter方法,再由doFilter方法决定是否调用Servlet程序的service方法
Filter链:
在一个web应用程序中可以有多个Filter程序,每个Filter程序可以对一个或一组Servlet程序进行拦截。如果有多个Filter程序对同一个Servlet程序进行拦截,当对该Servlet程序的请求到达时,web容器将多个Filter程序组成一个Filter链。Filter程序对Servlet的拦截顺序与其在web.xml中的映射顺序一致,上一个Filter程序的doFilter方法中的FilterChain.doFilter方法将激活下一个Filter程序的doFilter方法,最后一个Filter的doFIlter方法中的FilterChain.doFilter方法将激活目标Servlet的service方法
Filter接口:
三个方法:init(),doFilter(),destroy()
在web应用程序启动时,web服务器讲个据web.xml文件的配置信息来创建每个注册的Filter实例对象,并将其保存在内存中。Web应用程序创建Filter实例对象后将立即调用init()方法,即tomcat启动完毕后就已经调用了init()方法。当一个Filter对象能够拦截的请求到达后,Servlet容器将调用Filter对象的doFilter()方法。在当前的Filter对象的doFilter()方法内部需要调用FilterChain对象的doFilter()方法将请求交给Filter链中的下一个Filter或者目标Servlet程序。只有Filter对象的init()方法成功执行后,Filter对象才会被加入到Filter链中,该Filter对象才会被调用。
Filter的注册和运行:
Filter的注册于Servlet的注册基本一样,只是将Servlet换成filter就可以了,如下所示:
<filter>
<filter-name>firstFilter</filter-name>
<filter-class>FirstFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
Filter的映射,在web.xml文件中,使用<filter-mapping>元素设置一个filter所负责拦截的资源,一个filter拦截的资源可以通过两种方式来指定:Servlet的名称和资源的访问路径。
利用Servlet名称设置的方式如下:
<filter-mapping>
<filter-name>firstFilter</filter-name>
<servlet-name>default</servlet-name>
</filter-mapping>
<servlet-name>是某个Servlet在web.xml中注册的友好名称。
利用资源的访问路径设置的方式如下:
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/test.html</url-pattern>
</filter-mapping>
<url-pattern>指明当以这样一种访问路径访问资源时会使用filter。
我们知道,Servlet容器调用一个资源时有4中方式,分别是:
1. 通过正常的访问请求调用。
2. 通过RequestDispatcher.include方法调用
3. 通过RequestDispatcher.forward方法调用
4. 作为错误响应资源调用
相应的,可以设置filter,决定资源在以什么方式被调用时才进行拦截,这可以通过<dispatcher>元素指明,<dispatcher>元素只能取四个值,REQUEST,INCLUDE,FORWARD,ERROR分别对应之前说到的四种调用方式。在一个<filter-mapping>元素中可以设置多个<dispatche>子元素,用来指定filter对多种调用方式的拦截。例如:
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
在同一个web.xml中可以为同一个Filter设置多个映射,如果设置的多个映射都可以对某个资源进行拦截,那么这些Filter将构成一个针对该资源的Filter链,web容器按照如下的顺序构造Filter链:
1. 将通过<url-pattern>元素匹配的Filter加入到链中,如果有多个,按照它们在web.xml中的顺序依次加入。
2. 将通过<servlet-name>元素匹配的Filter加入到链中,如果有多个,按照它们在web.xml中的顺序依次加入。
如果为同一个Filter设置的多个<filter-mapping>元素都可以对某一资源进行拦截,那么所形成的Filter链中将多次出现同一个Filter,也就是说,该Filter的doFilter()方法将被调用多次,但是,该Filter的init()只被调用一次。
例子程序
Filter程序:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
public class FirstFilter implements Filter
{
private FilterConfig filterConfig = null;
private String paramValue=null;
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig=filterConfig;
paramValue=filterConfig.getInitParameter("encoding");
System.out.println("----------------init");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
//在命令行窗口中打印出所有请求头信息
System.out.println("Begin Headers-------------------------");
Enumeration headerNames=((HttpServletRequest)request).getHeaderNames();
while(headerNames.hasMoreElements())
{
String headerName=(String)headerNames.nextElement();
String value=((HttpServletRequest)request).getHeader(headerName);
System.out.println(headerName+":"+value);
}
System.out.println("End Headers---------------------------");
//在调用Servlet前写入response中的内容
response.setCharacterEncoding("gb2312");
response.setContentType("text/html");
PrintWriter out=response.getWriter();
String ip=request.getRemoteHost();
out.println("您的IP地址为:"+ip+"<br>");
chain.doFilter(request, response);
//在Servlet返回后写入response中的内容
out.println("<br>名称为encoding的初始化参数的值为:"+paramValue+"<br>");
out.println("当前web应用程序的真实路径为:"+filterConfig.getServletContext().getRealPath("/")+"<br>");
out.println(filterConfig.getServletContext()/*.getServletContextName()+"<br>"*/);
}
public void destroy()
{
this.filterConfig = null;
}
}
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>firstFilter</filter-name>
<filter-class>FirstFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/test.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<servlet-name>default</servlet-name>
</filter-mapping>
</web-app>