本篇我们讨论过滤器(Filter)。过滤器就是在源数据和目的数据之间起过滤作用的中间组件。过滤器可以截取客户端和资源之间的请求与响应消息,并对这些消息进行过滤。
当web容器接收到一个对资源的请求时,它先判断是否有过滤器与这个资源相连,如果由,容器会把请求交给过滤器处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息等。当目标资源对请求作出响应时,容器同样会将响应先转发给过滤器,在过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端。过滤器对客户端和目标资源来说是透明的。
可以在web应用程序中部署多个过滤器,组成过滤器链。过滤器链中的每个过滤器负责特定的操作和任务。过滤器的主要应用如下:
对用户请求进行统一认证
对用户的请求进行记录和审核
对用户发送的数据进行过滤和转换
转换图像格式
对响应内容进行压缩,以减少传输量
对请求和响应进行加密处理
触发资源访问事件
Filter API
和过滤器开发相关的接口与类都包含在javax.servlet和javax.servlet.http包中,主要有以下接口和类。
javax.servlet.Filter//接口 javax.servlet.FilterConfig//接口 javax.servlet.FilterChain//接口 javax.servlet.ServletRequestWrapper//类 javax.servlet.ServletResponseWrapper//类 javax.servlet.http.HttpServletRequestWrapper//类 javax.servlet.http.HttpServletResponseWrapper//类我们先介绍三个主要的接口。
Filter接口
开发过滤器要实现Filter接口。Filter接口定义了三个方法。
1)init(),web容器调用该方法来初始化过滤器。容器在调用该方法时,向过滤器传递FilterConfig对象,FilterConfig用法和ServletConfig相似。利用FilterConfig可以得到ServletContext对象,以及在部署描述文件中配置的过滤器的初始化参数。
2)doFilter,该方法类似于servlet的Service()方法。当客户端请求目标资源时,容器就会调用与这个目标资源管理的过滤器的doFilter()方法。在这个方法中,可以对请求和响应进行处理,处理完后,也可以调用chain.doFilter(request,response)将请求传递给下一个过滤器,也可以直接传回客户端。
3)destroy(),web容器调用该方法指示过滤器的生命周期结束。在这个方法中,可以释放过滤器使用的资源。
与开发servlet不同的是,Filter接口并没有相应的类可供继承,要开发过滤器,只能实现Filter接口。
FilterConfig接口
该接口由容器实现,类似于servlet的ServletConfig接口,用于在过滤器初始化时向其传递信息,在该接口中,定义了一下4个方法。
1)getFilterName(),得到在部署描述文件中指定的过滤器的名字
2)getInitParameter(String name),得到在部署描述文件中指定的名字为name的初始化参数的值
3)getInitParameterNames(),返回过滤器的所有初始化参数的名字的枚举集合。如果没有初始化参数,则返回空值。
4)getServletContext(),返回servlet上下文对象的引用。
FilterChain接口
该接口由容器实现,容器将其实例作为参数传入过滤器对象的doFilter()方法中。过滤器对象使用FilterChain对象调用过滤器链中的下一个过滤器,如果该过滤器是过滤器链的最后一个过滤器,那么将调用目标资源。它只有一个方法:
doFilter(request,response),调用该方法将使过滤链中的下一个过滤器被调用。如果调用该方法的过滤器是链中最后一个,那么目标资源将被调用。
过滤器的部署
在实现一个过滤器后,需要在部署描述文件中对过滤器进行配置。配置通过<filter>和<filter-mapping>来完成。一个配置描述文件的示例如下:
<?xml version='1.0' encoding='utf-8'?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <filter> <filter-name>TestFilter</filter-name> <filter-class>com.shan.filter.TestFilter</filter-class> <init-param> <param-name>word</param-name> <param-value>/WEB-INF/word.txt</param-value> </init-param> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/test.jsp</url-pattern> <servlet-name>servlet1</servlet-name> <servlet-name>*</servlet-name> <dispatcher>REQUEST</dispatcher> </filter-mapping> </web-app>
其中,<url-pattern>元素指定过滤器关联的URL样式,<servlet-name>指定过滤器对应的servlet。如果<filter-mapping>该值为*,表示对于任意的servlet。<filter-mapping>元素还可以包含最多4个<dispatcher>元素。该元素指定过滤器对应的请求方式,可以是REQUEST,INCLUDE,FORWARD,ERROR中的一个。默认是REQUEST。
现在我们来看一个过滤器的Demo程序。项目建立步骤和之前的完全相同,在此不再赘述。
编写过滤器类
package com.shan.filter; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class TestFilter implements Filter { public void init(FilterConfig config) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { response.setContentType("text/html;charset=gb2312"); PrintWriter out = response.getWriter(); out.println("Before doFilter()"); chain.doFilter(request,response); out.println("After doFilter()"); out.close(); } public void destroy() { } }
编写测试页面
<%@ page contentType="text/html;charset=gb2312" %> This is a test page..
编译和部署过滤器
<?xml version='1.0' encoding='utf-8'?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <filter> <filter-name>TestFilter</filter-name> <filter-class>com.shan.filter.TestFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/test.jsp</url-pattern> </filter-mapping> </web-app>
在dos环境下切换到工程所在目录,执行如下语句,对TestFilter类进行编译
javac -classpath D:\apache-tomcat-7.0.33\lib\servlet-api.jar;classes -d classes src\com\shan\filter\TestFilter.java
配置程序
在D:\apache-tomcat-7.0.33\webapps文件夹下新建TestFilter文件夹,在TestFilter文件夹下建立WEB-INF目录,将刚才编译后的classes文件夹拷贝到WEB-INF目录下。将该才新建的test.jsp拷贝到TestFilter文件夹下。
运行结果
在浏览器网址栏输入localhost:8080/TestFilter/test.jsp,结果如下:
可以看到,在输出This is a test之前,先执行了doFilter中输出语句,输出Before doFilter(),然后调用chain.doFilter(request,response)将请求传递给下一个过滤器,因为这之后没有其他过滤器,所以传递给目标资源,输出This is a test,在响应时又调用了doFilter语句,输出After doFilter()。
对请求和响应数据进行过滤
在留言板程序中,如果用户输入非法字符串,则应该对这些字符串进行屏蔽。不过HttpServletRequset类并没有提供对请求信息进行修改的setXXX()方法,而HttpServletResponse类也没有提供获取响应数据的方法,所以虽然过滤器可以截取到请求和响应对象,但是却无法直接使用这两个对象对他们的数据继续替换。不过,在servlet规范中,定义了4个包装类:ServletRequsetWrapper,ServletResponseWrapper,HttpServletRequsetWrapper,HttpServletReponseWrapper。这4个包装类分别实现了请求或响应的接口。它们在构造方法中接收真正的请求或响应对象,然后利用该对象的方法来完成自己需要实现的方法。
有了包装类,要修改请求或响应信息,就只需要编写一个包装类的子类,然后顾飞想要修改的方法即可。例如,我们想要为所有请求添加一个查询字符串,可以编写一个HttpServletRequestWrapper类的子类,并重写getQueryString()方法,然后在过滤器的doFilter()方法中,构造这个子类的对象,将其作为参数传递给chain.doFilter()方法即可。
对响应内容进行压缩的过滤器
网站的访问速度由很多因素决定,包括服务器性能,网络带宽,web程序响应速度,服务器与客户端网络传输速度等。从软件角度讲,我们要提高网站访问速度,即要尽可能提高web应用程序的执行速度,这可以通过优化代码来实现。如果要进一步提升网页浏览速度,那可以对响应内容进行压缩,以节省网络的带宽,提升访问速度。
目前主流浏览器都支持压缩后的网页内容,包括gzip,deflate压缩方式。浏览器和web服务器对于压缩网页的通信过程如下:
1)如果浏览器能接受压缩后的网页内容,那么它会在请求中发送Accept-Encoding报头,设置内容为gzip等。
2)web服务器读取Accept-Encoding请求报头的值来判断浏览器是否接受压缩,如果接受,则将目标页面响应内容进行压缩后再发送到客户端,同时设置Content-Encoding实体报头。
3)浏览器接收到响应内容后,按照Content-Encoding中的值对响应内容进行解压缩,然后显示。
我们可以通过过滤器来对目标页面的响应内容进行压缩,该过程类似于对请求和响应内容进行过滤的过滤器。实现原理就是使用包装类对象替换原始的响应对象,并使用java.util.zip.GZIPOutputStream作为响应内容的输出流对象。
转载请注明出处:http://blog.csdn.net/iAm333