Filter的基本功能是对Servlet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理的前后实现一些特俗功能,例如,纪录所有客户端的每次访问信息,同级静态HTML页面的访问次数,验证访问者的身份,修改Servlet容器传递给Servlet的请求信息,修改Servlet回送给Servlet容器的响应结果。
Filter的基本工作原理
Filter程序是一个实现了特殊接口(javax.servlet.Filter)的Java类,与Servlet程序相似,由Servlet容器进行调用和执行。
Filter程序需要在web.xml文件中进行注册和设置它所能拦截的资源,当Servlet容器开始调用某个Servlet程序时,如果发现已经注册了一个Filter程序来对该Servlet进行拦截,那么Servlet容器将不再直接调用Servlet的service方法,而是调用Filter的doFilter方法,再由doFilter方法决定是否去激活Servlet的service方法。在doFilter中调用FilterChain.doFilter方法来激活目标Servlet的service方法。如果没有调用,那么目标Servlet的service的方法就不会执行,在FilterChain.doFilter调用的前后增加一些处理代码,就可以实现Servlet响应前后的一些特殊处理。
Filter链
一个web程序中可以注册多个Filter程序,每个Filter程序可以对一个或一组Servlet程序进行拦截,如果有多个Filter程序对某个Servlet的程序的访问过程进行拦截,当针对该Servlet的访问请求到达时,这些拦截的FIlter程序就组成了一个Filter链,也称过滤器链。这个链的拦截顺序与他们在web.xml文件中的映射顺序一致,上一个Filter中的doFilter方法调用的FilterChain.doFilter将激活下一个Filter的doFilter方法,最后一个Filter激活目标Servlet的service方法。
Filter接口
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
FilterConfig对应web.xml中的配置:
<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所负责拦截的资源。这可以通过两种方式来指定:Servlet名称和资源的访问请求路径。
Servlet容器调用一个资源的方式有以下四种:
- 通过正常的访问请求调用
- 通过RequestDispatcher.include调用
- 通过RequestDispatcher.forward调用
- 作为错误响应资源调用
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<servlet-name>HelloServlet</servlet-name>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<url-pattern>和<servlet-name>是二选一的,<dispatcher>的值为上面所列出的4个。
如果一个Filter链中多次出现了同一个Filter程序,这个Filter程序的拦截处理过程将被执行多次
Filter的运行过程分析
-
Web服务器在启动加载Web应用程序时,将根据该Web应用程序的部署描述文件(web.xml)中的设置,为其中的每个<filter>元素创建一个相应的Filter实例对象,Filter实例对象被创建后,将一直驻留在内存中,直到Web应用程序卸载时才随之卸载。
-
Web服务器启动后,浏览器根据用户的请求生成HTTP请求消息,并将其发送给Web服务器。
-
Web服务器检查内存中是否存在负责处理当前请求的Servlet程序的实例,如果不存在,则加载和创建该Servlet对象。注意:Filter程序不会拦截Web服务器创建Servlet实例对象的过程。
-
Web服务器创建针对该次访问的请求对象和响应对象。请求对象中包含了HTTP的请求消息,响应对象用于封装将要发送的HTTP响应消息,响应消息的初始内容为空,但是以后可以调用它的各种方法来生成HTTP响应消息的各个部分。
-
如果存在可以拦截当前访问请求的Filter设置,Web容器将把这些Filter组装成一个拦截这次访问请求的Filter链,然后调用Filter链中的第一个Filter对象的doFilter方法,并将请求对象和响应对象传递给该方法。
-
Filter对象的doFilter方法的前置代码从请求对象中读取请求信息,这时也可以在响应对象中写入部分响应头和响应实体内容。
-
Filter对象的doFilter方法调用FilterChain.doFilter方法,通知Web容器把请求交给Filter链中的下一个Filter去处理,容器将以
filterChain.doFilter(servletRequest, servletResponse);
接收的参数作为传递给下一个Filter的请求和响应对象。 -
假定Filter链中只有一个Filter,容器将把请求交给目标Servlet程序去处理,即Web容器调用目标Servlet的service方法。
-
目标Servlet对象的service方法从请求消息中读取请求信息,并向响应对象中写入响应头和响应体信息。
-
目标Servlet对象的service方法执行完毕返回。
-
目标Servlet响应完成后,在Filter对象的doFilter方法调用FilterChain.doFilter方法返回。
-
Filter对象的doFilte方法继续执行其中的调用FilterChain.doFilter方法的语句后面的代码,这些后置代码仍然可以从请求对象中读取请求信息,也可以向响应对象中写入部分响应头和响应实体数据,执行完毕后返回。
-
Web服务器从响应对象中读取响应信息。
-
Web服务器将响应信息发送给浏览器处理和显示,一次请求响应过程完全结束,request和response变成垃圾,等待垃圾回收器将其彻底从内存中清除。
用Filter实现对资源的集中访问保护
如果一个网站的所有页面都是静态页面,但是我们又需要用户登录以查看,怎么办呢,如果是jsp页面,我们可以直接判断登录进行转发,如果是纯静态的话,我们就可以使用Filte了,在过滤器中进行登录判断和转发到登录页面的处理。
在Filter程序中修改请求和响应消息
- 修改Servlet容器传递给Servlet的请求信息
- 修改Servlet回送给Servlet容器的响应结果
要实现上述需求可以在doFilter中使用修饰后的request和response,使用装饰模式继承HttpServletRequestWrapper
和HttpServletResponseWrapper
。
用Filter实现文件上传请求的透明处理
我们知道,对于采用POST请求方式提交的multipart/form-data
类型的HTTP请求消息,在Servlet程序中无法调用HttpServletResponse实例对象的getParameter等方法来读取表单字段元素的信息。对于这种情况可以通过一个Filter程序进行预处理,让自定义的请求对象的getParameter等方法可以反悔multipart/form-data
编码类型的HTTP请求消息中的表单字段元素信息,从而让目标Servlet能够透明地使用request对象的getParameter方法。
用Filter实现响应正文的压缩
HTTP协议支持响应消息的实体内容进行压缩后再进行传递,以便减少网络传输的数据量,从而提高网络的传输效率。压缩的处理可以在Servlet程序中,当然最好是在Filter程序中。
HTTP协议中最常见的压缩方式是gzip和compress两种格式,如果浏览器程序支持对响应消息的压缩,它在请求消息中需要使用一个Accept-Encoding
头字段来指明其能够解码的数据压缩格式。如果Web服务器对响应消息中的实体内容进行了压缩,它需要使用Content-Encoding
头字段来指明压缩方式,浏览器将采用Content-Encoding
头字段中指定的压缩方式对接收到的实体内容进行解压后显示。
网友评论