Filter
在Web开发过程中,为了实现某些特殊的功能,经常需要对请求和响应消息进行处理。例如,记录用户访问信息,统计页面访问次数,验证用户身份等。Filter作为Servlet2.3中新增的技术,可以实现用户在访问某个目标资源之前对访问的请求和响应进行相关处理。
什么是Filter
Filter被称作过滤器或者拦截器,其基本功能就是对Servlet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理前后实现一些特殊功能。这就好比现实中的污水净化设备,它可以看做一个过滤器,专门用于过滤污水杂质。
当浏览器访问服务器中的目标资源时,会被Filter拦截,在Filter中进行预处理操作,然后再将请求转发给目标资源。当服务器接受到这个请求后会对其进行响应,在服务器处理响应的过程中,也需要先将响应结果发送给拦截器,在拦截器中对响应结果进行处理后,才会发送给客户端。
其实,Filter过滤器就是一个实现了javax.servlet.Filter接口的类,在javax.servlet.Filter接口中定义了三个方法。
- init(FilterConfig filterConfig):init()方法用来初始化过滤器,开发人员可以在init()方法中完成与构造方法类似的初始化功能,如果初始化代码中要使用到FilterConfig对象,那么,这些初始化代码就只能在Filter的init()方法中编写,而不能在构造方法中编写。
- doFilter(ServletRequest request,ServletResponse response,FilterChain chain):参数request和response为Web服务器或Filter链中的一个Filter传递过来的请求和响应对象;参数chain代表当前Filter链的对象,在当前Filter对象中的doFilter()方法内部需要调用FilterChain对象的doFilter()方法,才能把请求交付给Filter链中的下一个Filter或者目标程序去处理。
- destroy():destroy()方法在Web服务器卸载Filter对象之前被调用,该方法用于释放被Filter对象打开的资源。
这三个方法都是Filter的生命周期方法,其中,init()方法在Web应用程序加载的时候调用(服务器启动的时候),destroy()方法在Web应用程序卸载的时候调用(),这两个方法都只会被调用一次,而doFilter()方法只要有客户端请求时就会被调用,并且Filter所有的工作集中在doFilter()方法中。
第一个Filter程序
新建一个MyServlet.java
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("Hello MyServlet");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
在web.xml文件中对Servlet进行配置
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.eaglezsx.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
启动Tomcat服务器可以在浏览器中成功访问到MyServlet
新建一个MyFilter.java,来拦截MyServlet
public class MyFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
//过滤器对象在初始化时调用,可以配置一些初始化参数
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法会被调用
response.getWriter().write("Hello MyFilter");
}
public void destroy() {
//过滤器对象在销毁时自动调用,释放资源
}
}
在web.xml中进行配置
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.eaglezsx.servlet.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/MyServlet</url-pattern>
</filter-mapping>
使用浏览器访问MyServlet的时候,浏览器窗口只显示了MyFilter,没有显示MyServlet,说明MyFilter成功拦截了MyServlet。
Filter映射
filter-mapping元素用于配置一个Filter所负责拦截的资源。它有一个子元素dispatcher,该元素用于指定过滤器所拦截的资源被Servlet容器调用的方式dispatcher元素的值共有4个。
- REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器将不会被调用。(默认的是这种拦截方式)
- FROWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
- INCLUDE
- ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
新建一个ServletTest.java
public class ServletTest extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
request.getRequestDispatcher("/first.jsp").forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
新建一个first.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
first.jsp
</body>
</html>
新建一个FilterTest.java
public class FilterTest implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
response.getWriter().write("Hello FilterTest");
}
public void destroy() {
}
}
在web.xml,配置过滤器,拦截first.jsp
<filter>
<filter-name>FilterTest</filter-name>
<filter-class>com.eaglezsx.servlet.FilterTest</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest</filter-name>
<url-pattern>/first.jsp</url-pattern>
</filter-mapping>
之后访问ServletTest,浏览器显示为first.jsp。说明FilterTest没有拦截到ServletTest转发的first.jsp页面
为了拦截ServletTest通过forward()方法转发的first.jsp页面,需要在web.xml文件中增加一个dispatcher元素,将该元素值设置为FROWARD
<filter>
<filter-name>FilterTest</filter-name>
<filter-class>com.eaglezsx.servlet.FilterTest</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest</filter-name>
<url-pattern>/first.jsp</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
当在浏览器中再次访问ServletTest的时候,显示Hello FilterTest。由此可见ServletTest中通过forward()方法转发的first.jsp页面被成功拦截了。
Filter链
在一个Web应用程序中可以注册多个Filter程序,每个Filter程序都可以针对某一个URL进行拦截。如果多个Filter程序都对同一个URL进行拦截,那么这些Filter就会组成一个Filter链。Filter链用FilterChain对象来表示,FilterChain对象中有 一个doFilter()方法,该方法的作用就是让Filter链上的当前过滤器放行,请求进入下一个Filter。
当浏览器访问Web服务器中的资源时需要经过两个过滤器Filter1和Filter2,首先Filter1会对这个请求进行拦截,在Filter1过滤器中处理好请求后,通过调用Filter1中的doFilter()方法将请求传递给Filter2,Filter2将用户请求处理后同样调用doFilter()方法,最终将请求发送给目标资源。当Web服务器对这个请求做出响应时,也会被过滤器拦截,这个拦截顺序与之前相反,最终将响应结果发送给客户端。
MyFilter01.java
public class MyFilter01 implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
response.getWriter().write("MyFilter01</br>");
chain.doFilter(request, response);
}
public void destroy() {
}
}
MyFilter02.java
public class MyFilter02 implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
PrintWriter out=response.getWriter();
out.write("MyFilter02 Before</br>");
chain.doFilter(request, response);
out.write("</br>MyFilter02 After</br>");
}
public void destroy() {
}
}
web.xml文件
<filter>
<filter-name>MyFilter01</filter-name>
<filter-class>com.eaglezsx.servlet.MyFilter01</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter01</filter-name>
<url-pattern>/MyServlet</url-pattern>
</filter-mapping>
<filter>
<filter-name>MyFilter02</filter-name>
<filter-class>com.eaglezsx.servlet.MyFilter02</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter02</filter-name>
<url-pattern>/MyServlet</url-pattern>
</filter-mapping>
在浏览器访问MyServlet显示结果为
MyFilter01
MyFilter02 Before
Hello MyServlet
MyFilter02 After
MyServlet首先被MyFilter01拦截了,打印出MyFilter01中的内容,然后被MyFilter02拦截,直到MyServlet被MyFilter02放行后浏览器才显示出MyServlet中的输出内容。
Filter链中各个Filter的拦截顺序与它们在web.xml文件中filter-mapping元素的映射顺序一致。
FilterConfig接口
该接口封装了Filter程序在web.xml中的所有注册信息,并且提供了一系列获取这些配置信息的方法。
- String getFilterName():返回web.xml文件中为Filter所设置的名称,也就是返回filter-name元素设置的值。
- ServletContext getServletContext():返回FilterConfig对象中所包装的ServletContext对象的引用。
- String getInitParameter(String name):返回在web.xml文件中为Filter所设置的某个名称的初始化参数值,如果指定名称的初始化参数不存在,则返回null
- Enumeration getInitParameterNames():返回一个Enumeration集合对象,该集合对象中包含在web.xml文件中为当前Filter设置的所有初始化参数的名称。
MyFilter03.Java
public class MyFilter03 implements Filter{
private String characterEncoding;
FilterConfig fc;
public void init(FilterConfig filterConfig) throws ServletException {
this.fc=filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
characterEncoding=fc.getInitParameter("encoding");
System.out.println("encoding初始化参数为:"+characterEncoding);
chain.doFilter(request, response);
}
public void destroy() {
}
}
web.xml
<filter>
<filter-name>MyFilter03</filter-name>
<filter-class>com.eaglezsx.servlet.MyFilter03</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilter03</filter-name>
<url-pattern>/MyServlet</url-pattern>
</filter-mapping>
访问MyServlet,在控制台显示encoding初始化参数为:GBK