一、过滤器概述
过滤器是JavaWeb三大组件之一,它与Servlet很相似。对于Web应用程序来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和资源之间的请求和响应信息并对这些信息进行过滤。本文将对过滤器的和实现机制和应用进行简单的总结。
当Web容器接收到一个对资源的请求时,它将判断是否有过滤器与这个资源相关联,如果有那么容器将请求交给过滤器进行处理。下图所示为过滤器的工作过程。
在web应用程序中可以部署多个过滤器形成一个过滤器链,在请求资源时,过滤器链中的过滤器将依次对请求进行处理(执行顺序是在web.xml文件中的部署顺序),并将请求传递给下一个过滤器,直到目标资源。在发送响应时,则是按照相反的顺序对响应进行处理的,直到客户端。
过滤器的应用场景主要有几下几点:
- 执行目标资源之前做预处理工作,例如设置编码
- 通过条件判断是否放行,例如校验当前用户是否已经登录
- 在目标资源执行后做一些后续的处理工作,例如把目标资源输出的数据进行处理
二、一个简单过滤器的实现过程
写一个过滤器就是写一个类,并且这个类要实现Filter接口。Filter接口定义了三个方法,init()、doFilter()和destory()。它们的执行构成了过滤器的生命周期。
- init(FilterConfig):在服务器启动时会创建Filter实例,并且每个类型的Filter只创建一个实例,从此不再创建!在创建完Filter实例后,会马上调用init()方法完成初始化工作,这个方法只会被执行一次;
- doFilter(ServletRequest req,ServletResponse res,FilterChain chain):这个方法会在用户每次访问“目标资源(<url->pattern>index.jsp</url-pattern>)”时执行,如果需要“放行”,那么需要调用FilterChain的doFilter(ServletRequest,ServletResponse)方法,如果不调用FilterChain的doFilter()方法,那么目标资源将无法执行;
- destroy():服务器会在创建Filter对象之后,把Filter放到缓存中一直使用,通常不会销毁它。一般会在服务器关闭时销毁Filter对象,在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。
下面代码既是一个实现了Filter接口的过滤器:
public class FilterDemo1 implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { response.setContentType("text/html;charset=GB2312"); PrintWriter out=response.getWriter(); out.println("过滤器拦截你!"); chain.doFilter(request, response); out.close(); } public void destroy() { } }
过滤器写好之后要通过在web.xml文件对其进行配置,配置的方法类似于Servlet的。
<?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/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>day_0701</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.test.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/1.jsp</url-pattern> </filter-mapping> </web-app>
然后在WebContent目录下,新建一个jsp文件:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>在此处插入标题</title> </head> <body> 这是测试页面! </body> </html>
当请求访问1.jsp文件时,显示结果如下:
结果分析:因为上述过滤器是与1.jsp文件相关联,所以web容器将请求先交给过滤器进行处理,输出“过滤器拦截你”后,过滤器对请求进行“放行”,所以又输出“这是测试页面”。
三、过滤器的部署
过滤器的部署是在<filter>和<filter-mapping>元素来完成的。
3.1 过滤器的拦截方式
过滤器有四种拦截方式,分别是:REQUEST、FORWARD、INCLUDE、ERROR。
- REQUEST:直接访问目标资源时执行过滤器。包括:在地址栏中直接访问、表单提交、超链接、重定向,只要在地址栏中可以看到目标资源的路径,就是REQUEST;
- FORWARD:转发访问执行过滤器。包括RequestDispatcher#forward()方法、<jsp:forward>标签都是转发访问;
- INCLUDE:包含访问执行过滤器。包括RequestDispatcher#include()方法、<jsp:include>标签都是包含访问;
- ERROR:当目标资源在web.xml中配置为<error-page>中时,并且真的出现了异常,转发到目标资源时,会执行过滤器。
拦截方式是在<dispatcher></dispatcher>中设置的,默认情况是REQUEST方式。
3.2 设置目标资源
可以通过<url-pattern>或者<servlet-name>中设置目标资源,在<url-pattern>中指定过滤器关联的URL样式;在<servlet-name>指定过滤器指定的Servlet,用户在访问<url-pattern>元素指定的URL上的资源或<servlet-name>元素指定的Servlet时,该过滤器才会被容器调用。
<filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/2.jsp</url-pattern> <servlet-name>DemoServlet</servlet-name> </filter-mapping>
四、过滤器的应用举例
通过过滤器可以解决页面的乱码问题,代码如下:
public class EncodingFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 处理post请求编码问题 request.setCharacterEncoding("utf-8"); HttpServletRequest req = (HttpServletRequest) request; /* * 处理GET请求的编码问题 * 调包request * 1. 写一个request的装饰类 * 2. 在放行时,使用我们自己的request */ if(req.getMethod().equals("GET")) { EncodingRequest er = new EncodingRequest(req); chain.doFilter(er, response); } else if(req.getMethod().equals("POST")) { chain.doFilter(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }
/** * 装饰reqeust */ public class EncodingRequest extends HttpServletRequestWrapper { private HttpServletRequest req; public EncodingRequest(HttpServletRequest request) { super(request); this.req = request; } public String getParameter(String name) { String value = req.getParameter(name); // 处理编码问题 try { value = new String(value.getBytes("iso-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return value; } }