Servlet&JSP的那些事儿(十四)

本篇我们讨论过滤器(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,结果如下:

Servlet&JSP的那些事儿(十四)_第1张图片

可以看到,在输出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

你可能感兴趣的:(Servlet&JSP的那些事儿(十四))