第7章 Servlet 应用
JSP 是基于 Servlet 技术构架的,JSP 程序编译后会产生相应的 Servlet 类;通过加载 Servlet 类后才
能达到运行相应 JSP 程序的目的。因此,通过对本章的学习读者不但可以掌握直接开发 Servlet 应用程
序的方法,而且可以加深对 JSP 技术的理解和掌握。
本章涉及的内容包括:
Servlet 概述
Servlet 的开发和部署技巧
Servlet API
servlet 监听方法
session 监听方法
request 监听方法
Servlet 与 JSP 的关系
7.1 Servlet 概述
Servlet 是用 Java 语言编写、运行在服务器端的小程序。在本质上,Servlet 是一种 Java 类,但其编
写要符合 Servlet 自身的规范,使用 Java Servlet API(应用程序接口)及相关的类和方法。Servlet 是 CGI
的代替品,但它的功能和性能比 CGI 更加强大和完善。Servlet 的主要用途是实现对 Web 服务器功能的
扩充。它是一种动态加载的模块,采用请求-响应模式提供 Web 服务,以动态地生成的 Web 页面作为
响应服务请求的返回结果。
7.1.1 Servlet 的特点
Servlet 的特点主要体现在以下几个方面:
(1)高效性
Servlet 一旦被加载后,就一直驻留在内存中。因此,除了第一次加载时稍微慢了以外,此后处理
请求的速度都是很快的。另外,与传统的 CGI 相比,其高效性也是显然的。
(2)可移植性好
Servlet 使用的接口是 Java Servlet API,它是 Servlet 和服务器之间的一个标准接口,这使得 Servlet
具有跨服务器平台的良好特性。实际上,现在绝大多数的 Web 服务器都直接或通过插件来支持 Servlet。
(3)功能强大
Servlet 是一种 Java 类。除了 Java Servlet API 以外,Servlet 还可以使用用于扩展和添加到 API 的
Java 类软件包,它几乎可以使用 Java 语言的所有核心功能,完成一般 Java 程序所能完成的所有任务。
这些功能包括 Web 和 URL 访问、多线程、JDBC、RMI 等。
(4)安全性高
Servlet 是运行于服务器端的 Java 应用程序,其安全性可以从三个方面得到保障:首先,它可以使
用 Java 的安全框架;其次,Servlet API 被实现为类型安全的;此外,容器也会给 Servlet 的安全进行管
理。
(5)简洁性
基于 Java 语言的应用程序,其设计方法是采用面向对象技术的,封装性好,具有简洁、直观的代
码结构。
1
7.1.2 Servlet 的功能
Servlet 的应用涉及范围很广,可以实现的功能也非常多。下面将从 Servlet 的工作原理上对其功能
做一个总结。
(1)动态生成 HTML 格式的 Web 页面。Servlet 是请求-响应模式工作的,响应后产生的结果将
以 HTML 格式的 Web 页面返回给请求者。
(2)创建可嵌入到现有 HTML 页面中的一部分 HTML 页面(HTML 片段) 。
(3)可以与其他服务器资源(如文件、数据库、Applet、Java 应用程序等)进行通信。
(4)在单连接方式下传送数据时,允许在浏览器上打开服务器至 Applet(客户端小程序)的新连
接,并将该连接保持在打开状态;在客户机和服务器执行会话时,也允许 Applet 启动客户浏览器和服
务器之间的连接;可以通过定制协议或标准(如 IIOP)进行通信。
(5)对特殊的处理采用 MIME 类型来过滤数据。例如,图像转换和服务器端(包括 SSI) 。
(6)将定制的处理提供给所有服务器的标准例行程序。例如,Servlet 可以修改如何认证用户。
7.1.3 Servlet 的生命周期
Servlet 的生命周期就是它在 Web 服务器内存中“存活”的周期,即这个周期是从 Servlet 被加载到
内存开始、 一直延续到 Servlet 被终止或重新装入 Servlet 时结束。 一个 Servlet 的生命周期可以分为以下
几个阶段:
(1)加载阶段
加载实际上是一个瞬间的事,但在这个瞬间中却完成了许多工作。首先,服务器(服务器也理解为
Servlet 的容器)创建一个 Servlet 实例,然后调用 Servlet 的 init()方法对其进行初始化工作。
(2)请求服务阶段
对于提交过来的客户端请求,服务器调用 Servlet 实例的 service()方法来处理,最后以 HTML 格式
返回处理结果。service()方法还可以调用其他方法,如 doGet()、doPost()等。
(3)终止阶段
终止也是一个瞬间的事。终止操作是由服务器调用 Servlet 实例的 destroy()方法来完成的。当服务
器不再需要 Servlet,或重新装入 Servlet 的新实例时,驻留的 Servlet 实例就被终止。
7.2 Servlet 的开发和部署
运行 Servlet 所需要的准备工作比运行一般 Java 类的所需要的准备工作要复杂一些。这些工作主要
体现在对环境的配置上,包括对服务器的配置。稍有不慎,就会导致无法运行,或者运行失败。这对
Servlet 初学者来说,尤为重要。本节中,将通过一个简单的 Servlet 例子来说明开发和部署 Servlet 应用
程序的一般方法。
正如前文所述,一个 Servlet 本质上是一个 Java 类。因此,开发 Servlet 与开发一个 Java 类的方法
是一样的。不同的是,Servlet 有自己特殊的规范,要符合 Java Servlet API 标准等。这要在对 Servlet 的
不断深入学习中体会和掌握。
开发 Servlet 应用程序的步骤如下:
(1)编写 Servlet 应用程序的源代码
Servlet 应用程序的源代码即是 Java 类的源代码。在这里,我们编写一个名为 MyFirstServlet.java 的
Java 程序,保存在 D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目录下。其代码如下:
/*** MyFirstServlet.java 文件 ***/
package ch7;
import java.io.IOException;
2
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFirstServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException
{
response.setContentType("text/html; charset=gb2312"); //设置编码,解决中文问题
PrintWriter out=response.getWriter();
out.println("");
out.println("");
out.println("Servlet 应用程序");
out.println("");
out.println("");
out.println("Hello, 这是我的第一个 Servlet 应用程序!");
out.println("");
out.println("");
out.flush();
}
}
(2)编译源代码
这里的编译实际上也就是对 Java 代码的编译,使用的是下列命令:
javac MyFirstServlet.java
但在第一次编译 Servlet 的时候,可能会出现如图 7.1 所示的错误提示。这是由于编译时找不到相
应的包所致。解决方法是,将 D:\tomcat6\common\lib 目录下的文件 servlet-api.jar(低版本为 servlet.jar)
复制到 JDK 安装主目录下的\jre\lib\ext 目录中,然后执行上面 javac 命令即可。
另 外 , 还 需 要 注 意 , 编 译 后 产 生 的 类 文 件 — — MyFirstServlet.class 也 要 放 在
D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目录下。
3
图 7.1 编译 MyFirstServlet.java 的错误提示
(3)配置\WEB-INF\web.xml 文件
配置\WEB-INF\web.xml 文件的目的是完成 Servlet 类与 Servlet 名称之间的映射,以及 Servlet 名称
与相对路径的映射。
配置的具体操作是,在\WEB-INF\web.xml 文件(笔者机器上对应的文件是 D:\tomcat6\webapps
\ROOT\WEB-INF\web.xml)的标记中添加如下的两个标记:
serclassname
ch7.MyFirstServlet.class
和
4
serclassname
/myServlet
标记是用来创建 Servlet 类(class)与 Servlet 名称之间的映射。本例中,就是把名称为
serclassname 的 Servlet 映射到包 ch7 中的类 MyFirstServlet.class。
标记则是用于实现 Servlet 名称与相对路径的映射。本例中,就是把名称为
serclassname 的 Servlet 与相对路径/myServlet 对应起来。 这样, 如果在浏览器“地址”栏中出现/myServlet
的内容,则被映射为 MyFirstServlet.class 类。
注意,/myServlet 可以是服务器上存在或不存在的相对路径。
配置后,web.xml 文件的代码如下:
Welcome to Tomcat
Welcome to Tomcat
serclassname
ch7.MyFirstServlet
serclassname
/myServlet
(4)重启或启动 tomcat
如果 tomcat 已经处于运行状态,则需要对其重新启动,以使上述的配置生效。然后在 IE 浏览器的
“地址”栏中输入:
http://localhost:8080/myServlet
结果出现如图 7.2 所示的界面。
5
图 7.2 Servlet 的运行结果
7.3 熟悉 Servlet API
从前面的例子中可以发现,我们编写的代码都是放在 doGet()方法中。该方法是 HttpServlet 类提供
的方法之一。实际上,Servlet API 提供了大量的类和接口。熟悉 Servlet API 常用的类和接口是深入掌握
和运用 Servlet 技术的基础。本节将结合相关实例,对这些常用的类和接口进行介绍。
7.3.1 HttpServlet 类
HttpServlet 类主要用于处理 HTML 表单提交的数据, 它是 GenericServlet 类 的一个子类。
HttpServlet
类提供了 init()、destroy()、service() 等方法,其中 init() 和 destroy()是继承的方法。
(1)init()方法
在 Servlet 加载到内存时,init()方法被执行一次,此后该方法不再被执行。其作用是用于配置服务
器、做一些初始化工作等。例如,装载数据库驱动程序、初始化 Servlet 参数等。
init()方法可以缺省,也可以覆盖它。在调用 service()方法之前,要确保 init()方法已经执行完毕。
(2)service()方法
该方法是 Servlet 的核心,Servlet 的绝大部分工作是由它来完成。该方法定义如下:
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException
service()方法是 Servlet 的核心。当有一个 HttpServlet 对象被请求时,该对象的 service()方法就要被
调用。在调用时有两个参数被传递给 service()方法:一个是请求对象(HttpServletRequest) ,另一个是
响应对象 。
(HttpServletResponse) service()方法缺省的服务功能是调用与 HTTP 请求方法相应的 do 功能。
6
例如,HTTP 请求方法为 GET,则缺省情况下就调用 doGet()方法。Servlet 支持对 do 方法的覆盖。一般
来说,不必覆盖 service()方法,因为 service()方法是通过调用 do 等方法来完成任务的。
do 方法包括 doGet()方法和 doPost()方法。
doGet()方法
该方法用于获取服务器的信息。当客户端通过 HTML 表单发出一个 HTTP GET 请求或者直接访问
Servlet 的 URL 时,一般会调用 doGet()方法。传递给 doGet()方法的参数(如果有的话)将以属性-值对
的形式加到 URL 的后面,并与这个请求一起被发送给 Servlet。由于传递的参数在浏览器的“地址”栏
中可以看到,因此会引起系统安全方面的问题。
下面观察一个与 doGet()方法有关的例子。
例 7.1 调用 doGet()方法获取参数值的实例。
首先,编写一个 Servlet,Java 文件名为 doGetServlet.java,编译后产生的类 doGetServlet.class 保存
在 D:\tomcat6\webapps\ROOT\WEB-INF\ classes\ch7 目录下。其代码如下:
/*** doGetServlet.java 文件 ***/
package ch7;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class doGetServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out=response.getWriter();
out.println("输入的用户名:"+request.getParameter("userName"));
out.println("");
out.println("");
out.println("输入的密 码:"+request.getParameter("password"));
out.flush();
}
}
注意到,在 Servlet 中获取参数值的方法与在 JSP 文件中获取的方法是一致的,即都是利用
HttpServletRequest 的 getParameter()方法。
其次,修改 D:\tomcat6\webapps\ROOT\WEB-INF 目录下的 web.xml 文件,添加下列代码,以将该
Servlet 映射为/doGet:
doGet
ch7.doGetServlet
7
doGet
/doGet
最后, 其对应的 HTML 文件为 doGet.htm
编写客户端的表单, (保存在 D:\tomcat6\webapps\ROOT\ch7
目录下),代码如下:
输入数据页面——应用 doGet 方法的例子
用户名:
密 码:
需要注意的是,在客户端 HTML 文件的代码中,标记的 method 属性值应该为"get",action
属性值为 web.xml 中已经映射的 URL,即"/doGet"。此外,两个文本框的名称(name)必须与
doGetServlet.java 文件中设置的参数名一致。
完成上述工作以后,在 IE 浏览器中输入:http://localhost:8080/ch7/doGet.htm,并在打开页面的文本
框输入相应的字符(如,admin/123456) ,然后单击“提交”按钮。之后,数据被提交给有/doGet 指定
的 Servlet,在 Servlet 处理完了以后返回处理结果到客户端,如图 7.3 所示。
8
图 7.3 doGetServlet 的运行结果
从图 7.3 中可以看出,使用 doGet()方法时,传递的参数及其值都在浏览器的“地址”栏中看到。
因此,使用这种方法处理保密数据是不安全的。
doPost()方法
当客户端通过 HTML 表单发出一个 HTTP POST 请求时,service()方法会调用 doPost()方法来处理
这种请求。与该请求相关的参数将作为一个单独的 HTTP 请求从客户端发送到服务器端。因此,在使用
这种方法时,浏览器的“地址”栏中不会显示任何的参数信息。实际上,这种方法可以隐藏任何发送给
服务器的数据。因此,这种方法要相对安全一些,且 POST 方式适合传递大量的数据。
例 7.2 调用 doPost()方法获取参数值的实例。
该例子只需对例 7.1 稍微改动即可,即将例 7.1 中有关涉及 get 方法的代码改为相应的 post 方式即
可。结果得到如下的代码:
Servlet 代码:
/*** doPostServlet.java 文件 ***/
package ch7;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class doPostServlet extends HttpServlet
{
//改为 doPost()方法,其他不变
protected void doPost(HttpServletRequest request, HttpServletResponse response)
9
throws ServletException, IOException
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out=response.getWriter();
out.println("输入的用户名:"+request.getParameter("userName"));
out.println("");
out.println("");
out.println("输入的密 码:"+request.getParameter("password"));
out.flush();
}
}
在 web.xml 文件中添加的代码:
doPost
ch7.doPostServlet
doPost
/doPost
doPost.htm 文件:
输入数据页面——应用 doPost 方法的例子
用户名:
密 码:
10
运行 doPost.htm 文件,在打开的界面中输入相应的参数,然后单击“提交”按钮,Servlet 运行产
生的结果如图 7.4 所示。从图 7.4 中可以看到,浏览器的“地址”中已经没有任何的参数信息了,这些
参数信息在传递时都被隐藏了。
图 7.4 doPostServlet 的运行结果
Servlet 的响应可以是一个输出流,也可以是一个 HTTP 错误。对于前者,浏览器将根据它的内容
类型进行解释;对于后者,可重定向到另一个 URL、servlet、JSP。
需要注意的是,上面两个例子中分别显式地指定了 doGet()方法和 doPost()方法。doGet()方法只能
接收以 get 方式提交的表单数据; doPost()方法只能接收以 post 方式提交的表单数据。 如果在 Servlet 中,
使用 service()方法代替 doGet()方法或 doPost()方法,那么提交的数据可以采用 get 方式也可以采用 post
的方式,因为 service()方法能够自动根据提交的数据类型决定采用哪一个方法来处理客户端请求。
(3)destroy()方法
当服务器停止且 Servlet 被卸装时,destroy()方法被调用。该方法允许被覆盖,在 Servlet 的生命周
期内它也仅被执行一次。注意,该方法在调用之前,要确保 service()方法中的所有线程已全部终止或完
成,否则 destroy()方法不会被调用。
destroy()方法的主要任务是做一些“扫尾工作” 。例如,保存某一些变量的值,关闭数据库连接等。
(4)GetServletConfig()方法
该方法用于获取一个 ServletConfig 对象。 该对象可用于获取一些初始化参数和 ServletContext 接口,
后者可以提供有关 Servlet 的环境信息。
(5)GetServletInfo()方法
该方法用于获取有关 Servlet 的信息(如作者、版本等) ,返回类型是 String。它是一个可选方法。
例 7.3 使用多种方法的 Servlet 应用实例。
在本例中,将同时使用 init()、destroy()和 service()方法。通过该例子,可以理解这些方法工作的先
后顺序和作用等。
Servlet 对应的 Java 文件为 MultiMethodServlet.java,编译后生成的类——MultiMethodServlet.class
11
保存在 D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目录下。MultiMethodServlet.java 的代码如下:
/*** MultiMethodServlet.java 文件 ***/
package ch7;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MultiMethodServlet extends HttpServlet //implements HttpServlet
{
private String ShowInfo;
private String userInfo;
public void init(ServletConfig config) throws ServletException
{
ShowInfo = "简单的 Servlet 应用实例";
userInfo="";
System.out.println("初始化 ShowInfo!");
}
public void destroy()
{
ShowInfo = null;
System.out.println("释放资源等操作! ");
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException //使用 service()方法
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("Servlet 应用程序");
out.println("");
out.println("");
out.println("");
out.println(ShowInfo+"");
userInfo=request.getParameter("userInfo"); //获取客户端提交的数据
userInfo=new String(userInfo.getBytes("8859_1")); //解决中文问题
out.println(userInfo+"");
out.println("");
out.println("");
out.println("");
12
out.close();
}
}
D:\tomcat6\webapps\ROOT\WEB-INF 目下的 web.xml 文件中添加下列代码,已经将 Servlet 映射为
/MultiMethod:
MultiMethod
ch7.MultiMethodServlet
MultiMethod
/MultiMethod
客户端文件——MultiMethodServlet.htm 文件的 HTML 代码如下:
用户信息:
将 MultiMethodServlet.htm 保存在 D:\tomcat6\webapps\ROOT\ch7 目录下,并在 IE 浏览器中运行它,
然后在打开页面的文本框中输入“张无忌的基本信息:男,武当山人,大学学历” (或其他信息) ,并单
击“提交”按钮,该 Servlet 运行后产生的结果如图 7.5 所示。
13
图 7.5 MultiMethodServle 的运行结果
当在 MultiMethodServlet.htm 文件中,将 method="post"改为 method="get"时,执行上述的 Servlet
后在浏览器中显示的内容还是一样(当然,前提是输入要一样) 。不同的是,在浏览器的“地址”栏中
显示了所提交的参数信息,包括参数名和参数。这说明,service()能够自动识别提交数据所采用的方式,
并调用相应的请求处理方法。
另外,还注意到,init()和 destroy()输出的信息并没有在浏览器中出现。实际上,它们都是将信息输
出到控制台上。例如,在上述 Servlet 执行后,可以看到如图 7.6 所示控制台界面。在此界面中已经看
”
到了 init()方法输出的信息“初始化 ShowInfo!(最后一行) 。
14
图 7.6 MultiMethodServle 运行后的控制台
但是,当我们关闭所有的网页以后,在控制台上还是看不到我们想象当中的、由 destroy()方法输出
的信息——"释放资源等操作!"。这是为何?实际上,即使关闭所有的网页以后,相应的 Servlet 还没
有被终止,因为它还驻留在内存当中。Servlet 还没有被终止意味着 destroy()方法还没有被执行,所以看
不到预想的信息。为看到 destroy()方法输出的信息,我们可以通过关闭 tomcat(Ctrl+C)的方法来实现。
图 7.7 是关闭 tomcat 的瞬间所截获的界面。从中可以看出 destroy()方法输出的信息——"释放资源等操
作!"(最后一行) 。
15
图 7.7 关闭 tomcat 的瞬间
7.3.2 GenericServle 类
GenericServlet 类声明的格式如下:
public abstract class GenericServlet
extends Object
implements Servlet, ServletConfig, Serializable
GenericServlet 类定义类一个普通的、协议无关的 Servlet,它实现了 Servlet 与 ServletConfig 之间的
接口,可以直接用于扩展一个 Servlet。GenericServlet 使得 Servlet 的开发变得更为简单,它提供了生命
周期方法的运用提供了有效的途径。要编写一个 generic Servlet,必须重载抽象的 service 方法。
下面介绍 GenericServlet 类主要的一些方法:
(1)public void destroy()
该方法用于销毁当前的 Servlet。
(2)public String getInitParameter(String name)
用于以 String 类型返回由 name 指定的初始化参数的值。如果没有这样的参数,则返回 null。
(3)public Enumeration getInitParameterNames()
获取所有初始化参数的名称的集合。如果 Servlet 没有初始化参数,则返回空枚举对象。
getInitParameterNames()方法常用的代码:
Enumeration ParaSet=this.getInitParameterNames();
while (ParaSet.hasMoreElements())
{
16
String s= ParaSet.nextElement().toString();
out.println(s);
out.println(" = ");
out.println(this.getInitParameter(s));
}
(4)public ServletConfig getServletConfig()
该方法用于获取 ServletConfig 对象,该对象包含该 Servlet 的初始化参数、上下文和实例名。且通
过 ServletConfig 对象的 getInitParameter()方法也可以获取初始化参数。
(5)public ServletContext getServletContext()
该用法用于获取 Servlet 的上下文对象。
(6)public String getServletInfo()
获取该 Servlet 的信息,包括作者、版本和版权等信息。
(7)public String getServletName()
用于获取 Servlet 对象的名称。
(8)public void init (ServletConfig config) throws ServletException
或者 public void init() throws ServletException
该方法用于初始化 Servlet。在加载时被执行,且仅被执行一次。
(9)public abstract void service (ServletRequest req, ServletResponse res)
throws ServletException, IOException
service()是一个抽象方法,在 Servlet 中必须予以覆盖。其中,req 表示 ServletRequest 对象,包含客
户端请求;res 则表示 ServletResponse 对象,包含 Servlet 响应。该方法是抽象方法,GenericServlet 的
子类必须重写该方法。
service()是最重要的方法,程序员的许多劳动和智慧都将在这里得到体现。
下面考虑一个使用 GenericServlet 类实现的 Servlet,从中区别它与 HttpServlet 类在使用方法上的异
同。
例 7.4 用 GenericServlet 类来实现一个能够按指定格式进行时间查询的 Servlet。
假设 fm 为指定的时间格式,则按 fm 进行时间查询的核心代码如下:
Date d=new Date();
SimpleDateFormat dtFmt = new SimpleDateFormat(fm);
String dt=dtFmt.format(d);
其中,dt 为得到的时间字符串。
于是,我们可以利用 GenericServlet 类构造如下的 Servlet(testGenericServle.java)
/*** testGenericServle.java 文件 ***/
package ch7;
import java.io.IOException;
import java.util.Date;
import javax.servlet.*;
public class testGenericServlet extends GenericServlet
{
String fm;
17
public void init(ServletConfig config) throws ServletException
{
fm="yyyy 年 MM 月 dd 日,HH 时 mm 分 ss 秒";
}
public void destroy()
{
fm=null;
}
public String getServletInfo()
{
return "按指定格式查询当前时间";
}
public ServletConfig getServletConfig()
{
return null;
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html; charset=gb2312");
ServletOutputStream out=response.getOutputStream();
String str=request.getParameter("fm");
if(str!=null) fm=str;
fm=new String(fm.getBytes("8859_1"));
Date d=new Date();
java.text.SimpleDateFormat dtFmt=new java.text.SimpleDateFormat(fm);
String dt=dtFmt.format(d);
out.println("");
out.println("");
out.println("当前时间:"+dt);
out.println("");
out.println("(您使用的查询格式为:"+fm+")");
out.println("");
out.close();
}
}
在上述的 Servlet 中定义了五种方法,其中 service()方法是抽象方法,必须予以覆盖,其他方法可
根据需要设定。在 service()方法中有两个参数: request 和 response,它们分别为 ServletRequest 和
ServletResponse 类型的对象。其中,request 保存了客户端向服务器发送请求的相关数据;但在该例子中
Servlet 是把输出流传递给 ServletOutputStream 类型的对象 out,然后通过 out 对象将 Servlet 产生的结果
发回到客户端。
对 testGenericServle.java 进行编译,将产生的 testGenericServle.class 类放在 D:\tomcat6\webapps\
ROOT\WEB-INF\classes\ch7 目录下。
18
然后修改 D:\tomcat6\webapps\ROOT\WEB-INF 目录下的文件 web.xml, 将下列标记添加到该文件的
标记中,以完成将包 ch7 中 testGenericServlet 类映射到/testGenericServlet 的任务:
testGenericServlet
ch7.testGenericServlet
testGenericServlet
/testGenericServlet
最后,构造一个 HTML 文件——testGenericServlet.htm,用于输入时间格式。其代码如下:
输入时间格式:
将 testGenericServlet.htm 文件保存在 D:\tomcat6\webapps\ROOT\ch7 目录下,然后在 IE 浏览器的“地
址栏”中输入下列的 URL:
http://localhost:8080/ch7/testGenericServlet.htm
在打开的页面中输入如图 7.8 所示的时间格式,然后单击“提交”按钮。结果得到如图 7.9 所示的
查询效果。
19
图 7.8 设置时间格式
图 7.9 按给定时间格式的查询结果
7.3.3 Servlet 接口
Servlet 接口定义了生命周期的方法,首先用于 init()方法初始化 Servlet,然后调用 serviece()方法处
理请求, 最后使用 service()方法从服务器中移除 Servlet。Servlet 接口提供的方法与 HttpServlet 类提供的
20
方法几乎是一样的。这些方法主要包括:
public void init(ServletConfig config) throw ServletException
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException
public void destroy()
public ServletConfig getServletConfig()
public String GetServletConfig()
需要注意的是,Servlet 接口所定义的方法在 Servlet 当中必须予以实现。
其使用方法在前面已经介绍了,在这里将通过一个例子来说明它的应用。
例 7.5 Servlet 接口提供的方法开发 Servlet 应用程序。
为比较起见,本例实现的功能与例 7.1 的功能一样,都是接收来自客户端的数据,然后打印输出。
但例 7.1 中实现的 Servlet 是使用 HttpServlet 类来完成的; 该
而本例中则使用 Servlet 接口来实现。 Servlet
对应 ServletMethods.java 文件,其代码如下:
/*** ServletMethods.java 文件 ***/
package ch7;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ServletMethods implements Servlet // HttpServlet 类用关键字 extends
{
private String ShowInfo;
private String userInfo;
public void init(ServletConfig config) throws ServletException
{
ShowInfo = "简单的 Servlet 应用实例";
userInfo="";
System.out.println("初始化 ShowInfo!");
}
public void destroy()
{
ShowInfo = null;
System.out.println("释放资源等操作! ");
}
public String getServletInfo()
{
return "基于 Servlet 的应用软件 1.0 版本.";
}
public ServletConfig getServletConfig()
{
return null;
}
public void service(ServletRequest request, ServletResponse response) //此处的参数类型有变化
throws ServletException, IOException
21
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("Servlet 应用程序");
out.println("");
out.println("");
out.println("");
out.println(ShowInfo+"");
userInfo=request.getParameter("userInfo");
userInfo=new String(userInfo.getBytes("8859_1")); //解决中文问题
out.println(userInfo+"");
out.println("");
out.println("");
out.println("");
out.close();
}
}
ServletMethods.java 与 MultiMethodServlet.java 差异体现在:
在定义 ServletMethods 类时是使用关键字 implements,表示要实现 Servlet 接口;而在定义
MultiMethodServlet 类时则使用关键字 extends,表示要集成 HttpServlet 类;
两者在声明 service()方法时,其中参数的类型不一样,前者的参数为 ServletRequest request,
ServletResponse response , 后 者 的 参 数 为 HttpServletRequest request, HttpServletResponse
response。
ServletMethods 类中的五个方法都必须予以覆盖(实现) ,缺一不可。而 MultiMethodServlet 类
则不然。
7.3.4 ServletRequest 接口
ServletRequest 接口声明如下:
public interface ServletRequest
该接口定义了一个 ServletRequest 对象,通过该对象,Servlet 可以获取供客户端的请求信息。
ServletRequest 对象是由 Servlet 容器创建,在 Servlet 运行时它作为参数传递给 Servlet 的 service()方法。
ServletRequest 对 象 提 供 的 数 据 包 括 参 数 名 、 参 数 值 、 属 性 以 及 输 入 流 。 HttpServletRequest 是
ServletRequest 扩展的接口,可以提供 HTTP 数据。
ServletRequest 接口提供的方法主要包括:
(1)public Object getAttribute (String name)
用于获取由 name 指定名称的属性的值。
(2)public Enumeration getAttributeNames()
用于获取请求中所有属性的名称的集合。
例 7.6 利用 setAttribute()和 getAttribute()方法分别设置和读取 HttpServletReques 对象的属性值。
HttpServletRequest 是 ServletRequest 扩展的接口,要通过继承 HttpServlet 类来获得。构造的 Servlet
22
的代码如下:
/** SetGetAttriServlet.java 文件 **/
package ch7;
import java.util.*;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SetGetAttriServlet extends HttpServlet
{
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
request.setAttribute("name", "张三丰"); //设置对象 request 的属性
request.setAttribute("sex", "男");
request.setAttribute("ed_his", "博士研究生");
request.setAttribute("grade", "A");
response.setContentType("text/html; charset=gb2312");
PrintWriter out=response.getWriter();
Enumeration AttriNameSet=request.getAttributeNames();//获取 request 对象所有属性的名称
String AttriName;
out.println("");
while (AttriNameSet.hasMoreElements())
{
AttriName=AttriNameSet.nextElement().toString();
out.println(AttriName);
out.println(": ");
out.println(request.getAttribute(AttriName)); //按属性名称获取属性值
out.println("");
}
out.println("");
//根据指定的属性名 name 取该属性的值
out.println("属性 name 的值为:"+request.getAttribute("name"));
out.println("");
out.close();
}
}
对 SetGetAttriServlet.java 文 件 文 件 编 译 , 将 产 生 的 SetGetAttriServlet.class 类 保 存 在
D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目录下。然后修改 web.xml 文件,加入下列标记,以将
23
包 ch7 中的 SetGetAttriServlet 类映射为/SetGetAttriServlet:
SetGetAttriServlet
ch7.SetGetAttriServlet
SetGetAttriServlet
/SetGetAttriServlet
最后,重启 tomcat,在浏览器中输入:http://localhost:8080/SetGetAttriServlet,可以看到如图 7.10
所示的界面。
图 7.10 SetGetAttriServlet 运行的结果
(3)public String getCharacterEncoding()
获取请求中输入内容的字符编码方式,如果没有定义字符编码方式则返回空值。
(4)public int getContentLength()
获取请求内容的长度,如果长度未知则返回-1。
(5)public String getContentType()
获取请求内容的 MIME 类型,如果类型未知则返回 null。
例 7.7 获取请求内容的长度、编码方式和类型。
24
/*** EncConTyp.java 文件 ***/
package ch7;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncConTyp extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out=response.getWriter();
out.println("输入的用户名:"+request.getParameter("userName"));
out.println("");
out.println("");
out.println("输入的密 码:"+request.getParameter("password"));
out.println("");
String str;
str=request.getCharacterEncoding();
out.println("编码方式:"+str+"");
str=""+request.getContentLength();
out.println("内容长度:"+str+""); //具体长度跟客户端输入的字符多少有关
str=request.getContentType();
out.println("内容类型:"+str+"");
out.flush();
}
}
(6)public String getParameter (String name)
获取由 name 指定名称的属性的值。如果指定的参数不存在,则返回 null。该方法在前面已经多次
使用。
(7)public Map getParameterMap()
获取请求参数的参数映射。
例如,下列代码是对 getParameterMap()的一个应用:
Map map=request.getParameterMap(); //获取参数映射
Set set=ma.keySet(); //获取键集
Iterator it=set.iterator();
Object keyname, keyvalue;
25
while (it.hasNext())
{
keyname=it.next();
keyvalue=m.get(key);
out.println(keyname.toString()+":");
String[] AllValue=(String[])value;
int length= AllValue.length;
for (int i=1; i标记内加入下
列的标记:
ch7.ContextListenerServlet
(3)制作测试用的 JSP 文件
编辑一个 JSP 文件,对已部署的 Servlet 进行监听。此 JSP 文件名为 ContextListenerServlet.jsp,其
代码如下:
测试监听 Servlet!");
%>
将 ContextListenerServlet.jsp 文件保存在 D:\tomcat6\webapps\ROOT\ch7 目录下。
(4)运行、测试
在测试前,需要重新启动 tomcat。启动完毕后,在 IE 浏览器的“地址”中输入下列 URL:
http://localhost:8080/ch7/ContextListenerServlet.jsp
32
结果打开的页面如图 7.11 所示。
图 7.11 ContextListenerServlet.jsp 运行结果
在图 7.11 所示的页面中没有发现任何的监听信息。这是因为我们将监听信息输出到控制台上和文
本文件中。
在 关 闭 服 务 器 后 , 打 开 D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目 录 下 的 文 本 文 件
listenresult.txt,其中显示的信息如下:
2007 年 08 月 08 日,12 时 51 分 25 秒: 监听程序被初始化!
2007 年 08 月 08 日,12 时 51 分 28 秒: 增加了一个 ServletContext 属性:attributeAdded('userName', '
张山丰')
2007 年 08 月 08 日,12 时 51 分 28 秒: 删除了一个 ServletContext 属性:
attributeRemoved('userName',
'张山丰')
2007 年 08 月 08 日,12 时 51 分 28 秒: 增加了一个 ServletContext 属性:attributeAdded('userName', '
张三丰')
2007 年 08 月 08 日,12 时 51 分 28 秒: 增加了一个 ServletContext 属性:attributeAdded('sex', '女')
2007 年 08 月 08 日,12 时 51 分 28 秒: 某 ServletContext 属性被改变:attributeReplaced('sex', '女')
2007 年 08 月 08 日,12 时 51 分 28 秒: 增加了一个 ServletContext 属性:attributeAdded('grade', '99')
2007 年 08 月 08 日,12 时 55 分 10 秒: 监听程序被销毁!
7.4 创建网站访问计数器
前面介绍了使用 application 对象来创建网站访问计数器的方法。但这种方法存在一个严重的缺点:
当 Web 服务器重启后,计数器的值重新从 0 开始计数。这与实际应用有脱节。因为在实际应用中,因
故障或停电等原因而需要关闭服务器的情况是很多的。显然,我们不希望出现这种情况后,服务器的计
数器从 0 开始计数,而是希望它能够在原有值的基础上进行计数。
33
解决这个问题的基本思路是:当关闭(正常关闭)服务器的时候将,计数器的值保存在一个指定的
文件中;在启动服务器的时候,则从该文件中读取该数值并以之对计数器初始化。这样,就可以使得即
使在服务器重启后,计数器仍然能够在原有值的基础上进行计数。
要成上述功能,需要服务器能够完成两件事情: (1)在启动的时候能读取指定的文件; (2)在关闭
的时候能写到指定的文件。
根据前面对 ServletContextListener 接口的介绍可知,这两件事情可以分别在 contextInitialized()和
contextDestroyed()方法中完成。
但是,这里有一个计数难点,就是如何在 Servlet 中(具体将,是如何在 contextInitialized()方法和
contextDestroyed()方法中)获取 application 对象。
这 里 介 绍 的 一 种 方 法 就 是 , 利 用 ServletContextEvent 对 象 的 getServletContext() 方 法 来 获 取
application 对象,进而实现对此对象的访问。例如,在 ServletContextListener 接口的 contextInitialized()
方法中,可以用下列代码来完成对 application 对象的访问。
public void contextInitialized(ServletContextEvent sce)
{
context = sce.getServletContext();
String writeStr=context.getAttribute("count").toString();
……
}
于是,我们就可以构造一个用于实现计数器功能的 Servlet(WebAccessCount.java),其代码如下:
/** WebAccessCount.java 文件 **/
package ch7;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import java.io.*;
public class WebAccessCount
implements ServletContextListener
{
private ServletContext context = null;
public void contextInitialized(ServletContextEvent sce)
{
String path="D:\\tomcat6\\webapps\\ROOT\\WEB-INF\\classes\\ch7\\n.txt";
String nStr="";
try
{
BufferedReader inStream=new BufferedReader(new FileReader(path));
nStr=inStream.readLine();
inStream.close();
inStream=null;
34
} catch (IOException e)
{
nStr="0";
}
context = sce.getServletContext();
context.setAttribute("count", nStr);
}
public void contextDestroyed(ServletContextEvent sce)
{
context = sce.getServletContext();
String path="D:\\tomcat6\\webapps\\ROOT\\WEB-INF\\classes\\ch7\\n.txt";
try
{
context = sce.getServletContext();
String writeStr=context.getAttribute("count").toString();
PrintWriter myfile=new PrintWriter(new FileOutputStream(path,false));
myfile.write(writeStr);
myfile.close();
myfile=null;
}
catch(IOException e) {}
for(;1==1;);
}
}
编 译 上 述 的 WebAccessCount.java 文 件 后 , 将 产 生 的 WebAccessCount.class 文 件 保 存 在
D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目录下。
其次,修改 D:\tomcat6\webapps\ROOT\WEB-INF 目录下的文件 web.xml,在其中的标记
中添加下列标记:
ch7.WebAccessCount
最后,编写测试用的 JSP 文件——WebAccessCoun.jsp,代码如下:
35
将 WebAccessCoun.jsp 文件保存在 D:\tomcat6\webapps\ROOT\ch7 目录下。测试时,先启动 Web 服
务器(tomcat) ,然后在 IE 浏览器的“地址”栏中输入下列地址:
http://localhost:8080/ch7/WebAccessCoun.jsp
运行后将看到如图 7.12 所示的页面。
图 7.12 首次运行 WebAccessCoun.jsp 时的页面
为观察效果,可多次打开 WebAccessCoun.jsp,或多次刷新它。然后关闭服务器,又重新启动它,
将发现计算器是在关闭服务器前的数值的基础计数的。因此,这种计数器较好满足实际应用的需要。
7.5 session 监听
会话(session)监听是指监听会话中的活动情况、属性设置情况、active 和 paasivate 情况等。这种
36
监 听 通 常 要 涉 及 到 四 种 接 口 : HttpSessionListener 、 HttpSessionActivationListener 、
HttpSessionAttributeListener 和 HttpSessionBindingListener 接口。以下先对这四种接口进行简要说明,然
后再介绍会话监听的实现方法。
7.5.1 与 session 监听相关的四个接口
1 HttpSessionListener 接口
该接口用于监听会话的创建和销毁等活动。它定义了两种方法:
sessionCreated(SessionEvent event)方法: Web 应用程序内创建一个会话时,
当 该方法将被激发;
sessionDestroyed(HttpSessionEvent event)方法:当销毁一个会话时,该方法将被激发。
2 HttpSessionActivationListener 接口
该接口用于监听会话的 active、paasivate 等活动。它也定义了两种方法:
sessionDidActivate(HttpSessionEvent arg0)方法:当会话转移到其他服务器之后,该方法将被激
发;
sessionWillPassivate(HttpSessionEvent arg0)方法:当会话转移到其他服务器之前,该方法将被
激发。
3 HttpSessionAttributeListener
该接口用于监听会话种对属性的操作情况。它与监听上下文的 ServletContextAttributeListener 接口
非常类似。不同的是,该接口是对一个会话过程进行监听,是面向单用户的;而
ServletContextAttributeListener 接口是对 Servlet 所处的运行环境进行监听,是面向多用户的。
该接口定义了三种方法:
attributeAdded(HttpSessionBindingEvent event) 方法: 在会话中增加一个属性时,该方法被激发;
attributeRemoved(HttpSessionBindingEvent event)方法:在会话中删除一个属性时,该方法被激
发;
attributeReplaced(HttpSessionBindingEvent event) 方法:在会话中重新设置一个属性时,该方
法被激发。
4 HttpSessionBindingListener 接口
该接口用于监听会话中对象的绑定情况。它定义了两个方法:
valueBound(HttpSessionBindingEvent event)方法:当某个对象被绑定到会话当中时,该方法将
被激发;
valueUnbound(HttpSessionBindingEvent event)方法:当某个对象被从会话中移出时(通过调用
,该方法将被激发。
session.invalidate()、session.removeAttribute()或 session 自动过期时)
7.5.2 session 监听程序
session 对在线用户的管理非常有用。因为现在的系统在登录后,每一个用户都变成了一个 sessiion
对象。通过对这些 session 对象的监听,可以获取他们所有的活动信息。从这些信息中我们可能发掘出
意想不到的、对生产实践具有重要指导意义的知识。当然,这已超越了本书的内容,属于数据挖掘的范
畴。在本小结中,我们主要介绍如何对 session 会话进行监听、以及如何实现对已有 session 对象及其它
们属性的管理。
例 7.9 编写一个 Web 应用程序,使之能够完成下列功能:
session 对象管理。显示当前 Web 应用中所有的 session 对象,允许销毁任意一个 session 对象;
session 对象的属性管理。可以查看任意一个 session 对象的属性,并允许对指定对象的属性进
行添加、删除和属性值的修改操作。
37
监听上述的每一个操作,并输出相应的提示信息。
该 Web 应用可按照下列步骤来完成:
(1)构造监听 Servlet
通过实现上面介绍的四种接口,编写一个用于对 session 对象的创建、销毁、设置和删除属性等操
作进行监听的 Servlet;同时它也被当作一般的 Java 类来使用,以获取系统当前所有的或指定的 session
对象。对应的 Java 文件为 SessionListenServlet.java,代码如下:
/** SessionListenServlet.java 文件 **/
package ch7;
import java.util.Hashtable;
import java.util.Iterator;
import java.io.*;
import javax.servlet.http.*;
public class SessionListenServlet implements HttpSessionListener, HttpSessionActivationListener,
HttpSessionAttributeListener, HttpSessionBindingListener
{
static Hashtable SessionSet = new Hashtable(); //定义散列表,保存所有 session 对象的引用
String WriteStr=null;
int sessionNumber=0; //记录当前 session 对象个数
public void sessionCreated(HttpSessionEvent arg0)
{
sessionNumber++; //session 对象个数加 1
HttpSession session = arg0.getSession();
WriteStr="创建了一个 session 对象,id= "+session.getId()+" (目前一共有 "+sessionNumber+"
个 session 对象)";
WriteListenResult(WriteStr);
SessionSet.put(session.getId(), session);
}
public void sessionDestroyed(HttpSessionEvent arg0)
{
sessionNumber--; //session 对象个数减 1
HttpSession session = arg0.getSession();
WriteStr="销毁了一个 session 对象,id= "+session.getId()+" (目前一共有 "+sessionNumber+"
个 session 对象)";
WriteListenResult(WriteStr);
SessionSet.remove(session.getId());
}
public void sessionDidActivate(HttpSessionEvent arg0)
{
HttpSession session = arg0.getSession();
WriteStr="session 对象(id=" +session.getId()+ ")准备发生转移!";
WriteListenResult(WriteStr);
}
public void sessionWillPassivate(HttpSessionEvent arg0)
38
{
HttpSession session = arg0.getSession();
WriteStr="session 对 象 (id=" +session.getId()+ ") 已 经 发 生 转 移 !"+"( 目 前 一 共 有
"+sessionNumber+" 个 session 对象)";
WriteListenResult(WriteStr);
}
public void attributeAdded(HttpSessionBindingEvent event)
{
HttpSession session = event.getSession();
WriteStr=" 属 性 "+event.getName()+"( 值 为 "+event.getValue()+") 被 加 入 session 对 象 中
(id="+session.getId()+")!";
WriteListenResult(WriteStr);
}
public void attributeRemoved(HttpSessionBindingEvent event)
{
HttpSession session = event.getSession();
WriteStr=" 属 性 "+event.getName()+"( 值 为 "+event.getValue()+") 被 从 session 对 象
(id="+session.getId()+")中删除!";
WriteListenResult(WriteStr);
}
public void attributeReplaced(HttpSessionBindingEvent event)
{
HttpSession session = event.getSession();
WriteStr="session 对 象 (id="+session.getId()+") 中 的 属 性 "+event.getName()+"( 值 为
"+event.getValue()+")被修改!";
WriteListenResult(WriteStr);
}
public void valueBound(HttpSessionBindingEvent event)
{
HttpSession session = event.getSession();
WriteStr="session 对 象 (id="+session.getId()+") 绑 定 了 对 象 "+event.getName()+"( 值 为
"+event.getValue()+")!";
WriteListenResult(WriteStr);
}
public void valueUnbound(HttpSessionBindingEvent event)
{
HttpSession session = event.getSession();
WriteStr="session 对 象 (id="+session.getId()+") 解 除 了 对 对 象 "+event.getName()+"( 值 为
"+event.getValue()+")的绑定!";
WriteListenResult(WriteStr);
}
//-------------------------------------------
static public Iterator getAllSession() //返回所有的 session 对象
{
39
return SessionSet.values().iterator();
}
static public HttpSession getSessionbyId(String sessionId) //按 id 返回指定的 session 对象
{
return (HttpSession) SessionSet.get(sessionId);
}
//------------------------------------------------------
private void WriteListenResult(String writeStr) //写文本文件的函数
{
String fm="yyyy 年 MM 月 dd 日,HH 时 mm 分 ss 秒";
java.util.Date d=new java.util.Date();
java.text.SimpleDateFormat dtFmt=new java.text.SimpleDateFormat(fm);
String dt=dtFmt.format(d);
try
{
String path="D:\\tomcat6\\webapps\\ROOT\\WEB-INF\\classes\\ch7\\Sessionlistenresult.txt";
PrintWriter myfile=new PrintWriter(new FileOutputStream(path,true)); //以追加方式打开文件
myfile.write(dt+": "+writeStr+"\r\n"); //将监听信息写到文件中
myfile.close();
System.out.println(dt+": "+writeStr+"\r\n"); //将监听信息输出到控制台上
}catch(IOException e){System.out.println("IOException 异常:"+e.toString());}
}
}// end
编译 SessionListenServlet.java 后,将产生的 SessionListenServlet.class 类保存在 D:\tomcat6\webapps\
ROOT\WEB-INF\classes\ch7 目录下。
(2)编写 session 对象查看页面文件(SessionListenServlet.jsp)
该页面用于显示当前 Web 应用中所有的 session 对象,可以对当前 session 对象进行命名,并提供
销毁 session 对象和查看 session 对象的超链接。该页面对象的 JSP 文件为 SessionListenServlet.jsp,保存
在 D:\tomcat6\webapps\ROOT\ch7\SessionListen 目录下。图 7.13 是该文件运行时的一个界面。
40
图 7.13 SessionListenServlet.jsp 运行的一个界面
文件 SessionListenServlet.jsp 的代码如下:
session 对象查看页面
41
是否为当前会话
会话 id
用户名
创建时间
操作
查看属性
");
out.println("");
if(session1.getId()==session.getId())
{
out.println("是");
}
else
{
out.println("否");
}
out.println("");
out.println("" + session1.getId() + "" );
out.println("" + session1.getAttribute("name") + "" );
out.println("" + session1.getCreationTime() + "" );
%>
'>销毁 session
'>属性>>>
");
}
catch(Exception ex)
{
out.println(ex.toString());
return;
}
}
%>
42
修改当前会话的用户名:
(3)实现销毁 session 对象的功能(DestroySession.jsp)
当在图 7.13 所示的页面中点击超链接“销毁 session”时,相应 session 对象的 id 将被传到
DestroySession.jsp 对应的页面。DestroySession.jsp 的作用就是根据接收到的 id,销毁相应的 session 对
象, 然后重定向到 session 对象查看页面 。
(SessionListenServlet.jsp) 这样,在视觉上好像都是在“session
对象查看页面”上进行删除操作,且每点击一次 session 对象就少了一个。
DestroySession.jsp 的代码如下:
销毁 session 对象
(4)实现销毁 session 对象的功能(DestroySession.jsp)
,
如 果 在 图 7.13 所 示 的 页 面 中 点 击 超 链 接 “ 属 性 >>> ” 则 将 相 应 session 对 象 的 id 传 到
displayattribute.jsp 页面。该页面根据 id 号列出相应对象的所有属性及其值,并且允许在此页面中添加
属性、修改属性和删除属性等。
图 7.14 是 DestroySession.jsp 文件运行时的一个界面。
43
图 7.14 DestroySession.jsp 文件运行的界面
DestroySession.jsp 文件的代码如下:
销毁 session 对象
session 对象查看页面
当前的 session 对象:");
out.println("");
out.println(" id = "+session1.getId());
out.println("");
out.println(" 对象名(用户设置)= "+session1.getAttribute("name"));
out.println(""); out.println("");
%>
属性名
属性值
是否移出
");
out.println(" ");
out.println(s);
out.println("");
out.println(" ");
45
out.println(session1.getAttribute(s));
out.println("");
out.println(" ");
%>
&sessionid=">移出
");
out.println("");
}
%>
该 session 对象总共包含个 属性。
">
属性名:
属性值:
返回 session 对象查看页面
以上三个 JSP 文件 SessionListenServlet.jsp、DestroySession.jsp 和 displayattribute.jsp 都保存在
D:\tomcat6\webapps\ROOT\ch7\SessionListen 目录下。
(4)部署文件
打开 D:\tomcat6\webapps\ROOT\WEB-INF 目录下的 web.xml 文件, 在其中的中加入下列
标记:
ch7.SessionListenServlet
(5)运行程序、查看结果
首先重新启动 web 服务器(tomcat),然后在浏览器“地址”栏中输入下列的 URL 并回车:
http://localhost:8080/ch7/SessionListen/SessionListenServlet.jsp
连续进行两次这样的操作后,打开的页面如图 7.15 所示。图中包含两行信息,表示当 Web 应用中
存在两个 session 对象。这是因为,每一启动一个 JSP 页面就产生一个 session 对象。
46
图 7.15 创建两个 session 对象
当在如图 7.15 所示的页面中点击某一个超链接“销毁 session”时,对应的 session 对象将被删除。
在本例中,我们点击第一行中的“销毁 session”。
然后,点击剩下一行中的超链接“属性>>>” ,在打开的页面中添加两个属性,结果如图 7.16 所示。
47
图 7.16 为 session 对象添加属性
接着在图 7.16 所示的页面中单击第二行中的超链接“移出” ,表示从对应的 session 对象中删除相
应的属性。
这样,我们总共对 Web 应用中的 session 对象进行了以下的几个操作:
创建两个 session 对象;
销毁一个 session 对象;
为剩下的 session 对象添加两个属性;
删除 session 对象中的一个属性。
下面我们观察监听 Servlet 获取的信息。这些信息可以在服务器的控制台中查看,也可以打开
D:\tomcat6\webapps\ROOT\WEB-INF\classes\ch7 目下的 Sessionlistenresult.txt 文件来查看。结果都将看到
如下的一些信息,其描述与我们所进行的操作是一致的:
2007 年 08 月 09 日,16 时 20 分 05 秒: 创建了一个 session 对象, D843744A17E804ED4E1CEC64
id=
E2048CBD (目前一共有 1 个 session 对象)
2007 年 08 月 09 日,16 时 20 分 11 秒: 创建了一个 session 对象,id= 5D17421496CB47471CD7A054
163D11E0 (目前一共有 2 个 session 对象)
48
2007 年 08 月 09 日,16 时 27 分 28 秒: 销毁了一个 session 对象, D843744A17E804ED4E1CEC64
id=
E2048CBD (目前一共有 1 个 session 对象)
2007 年 08 月 09 日,16 时 27 分 39 秒: 属性 myAttribute1(值为 myValue1)被加入 session 对象中(i
d=5D17421496CB47471CD7A054163D11E0)!
2007 年 08 月 09 日,16 时 27 分 44 秒: 属性 myAttribute2(值为 myValue2)被加入 session 对象中(i
d=5D17421496CB47471CD7A054163D11E0)!
2007 年 08 月 09 日,16 时 32 分 25 秒: 属性 myAttribute1(值为 myValue1)被从 session 对象(id=5D
17421496CB47471CD7A054163D11E0)中删除!
7.6 request 监听
request 监听是指对客户端的请求进行监听。通过 request 监听,可以对客户端的请求进行相应的处
理。例如,我们可以通过 request 监听获取客户端的 IP 地址,根据已有经验对 IP 地址进行分类,以确
定哪些是恶意用户,哪些是友好用户;对哪些用户应禁止访问,或有条件允许哪些用户访问哪些资源等
等。
下面先简单介绍涉及 request 监听的两个接口,然后在介绍相应的监听程序。
7.6.1 与 request 监听相关的两个接口
1 ServletRequestListener 接口
该接口定义了两个方法:
requestInitialized(ServletRequestEvent rre)方法:当 request 对象被创建时,该方法被调用;
requestDestroyed(ServletRequestEvent rre)方法:当 request 对象被销毁时,该方法被调用。
2 ServletRequestAttributeListener 接口
该接口定义了三个方法:
attributeAdded(ServletRequestAttributeEvent srae)方法:当在 request 对象中添加一个属性时,该
方法被调用;
attributeRemoved(ServletRequestAttributeEvent srae)方法:当将一个属性从 request 对象中删除
时,该方法被调用;
attributeReplaced(ServletRequestAttributeEvent srae)方法: request 对象中的一个属性的值被替
当
换(修改)时,该方法被调用。
7.6.2 request 监听程序
下面是一个用于监听 request 活动情况的 Servlet。编译后,对其部署(修改 web.xml 文件),然后运
行任意涉及到 request 对象的 JSP 文件,都将看到相应的监听信息。具体操作方法与其他监听程序类似,
此不赘述。
以下是相应 Servlet 的源代码:
/** RequestListenServlet.java 文件 **/
package ch7;
import javax.servlet.*;
public class RequestListenServlet implements ServletRequestListener,ServletRequestAttributeListener
49
{
private String WriteStr=null;
//实现 ServletRequestListener 接口
public void requestInitialized(ServletRequestEvent rre)
{
WriteStr="用户的 IP:"+rre.getServletRequest().getRemoteAddr();
WriteListenResult(WriteStr);
}
public void requestDestroyed(ServletRequestEvent rre)
{
WriteStr="销毁 request 对象!";
WriteListenResult(WriteStr);
}
//实现 ServletRequestAttributeListener 接口
public void attributeAdded(ServletRequestAttributeEvent srae)
{
WriteStr="request 对象中加入了属性"+srae.getName()+"(值为"+srae.getValue()+") !";
WriteListenResult(WriteStr);
}
public void attributeRemoved(ServletRequestAttributeEvent srae)
{
WriteStr="request 对象中的属性"+srae.getName()+"(值为"+srae.getValue()+")被移出!";
WriteListenResult(WriteStr);
}
public void attributeReplaced(ServletRequestAttributeEvent srae)
{
WriteStr="request 对象中属性"+srae.getName()+"(原值为"+srae.getValue()+")的值被修改!";
WriteListenResult(WriteStr);
}
//------------------------------------------------------
private void WriteListenResult(String writeStr) //写文本文件的函数
{
String fm="yyyy 年 MM 月 dd 日,HH 时 mm 分 ss 秒";
java.util.Date d=new java.util.Date();
java.text.SimpleDateFormat dtFmt=new java.text.SimpleDateFormat(fm);
String dt=dtFmt.format(d);
try
{
String path="D:\\tomcat6\\webapps\\ROOT\\WEB-INF\\classes\\ch7\\Requestlistenresult.txt";
PrintWriter myfile=new PrintWriter(new FileOutputStream(path,true)); //以追加方式打开文件
myfile.write(dt+": "+writeStr+"\r\n"); //将监听信息写到文件中
myfile.close();
System.out.println(dt+": "+writeStr+"\r\n"); //将监听信息输出到控制台上
50
}catch(IOException e){System.out.println("IOException 异常:"+e.toString());}
}
}
7.7 Servlet 与 JSP
学习了 Servlet 技术之后,也许有的读者反而对 JSP 的理解模糊起来:Servlet 程序和 JSP 页面都需
要 Java 语言来编写,它们好像并没有太多的区别啊。
我们先来看下面的例子。
该例子是在网页上显示一张关于学生信息的表格,该表格如表 7.1 所示。
表 7.1 关于学生信息的表格
我们可以使用 JSP 页面来实现此显示功能。假设该 JSP 页面对应的文件为 ServletJSP.jsp,其代码如
下:
姓名
张无忌
51
性别
男
成绩
也可以使用 Servlet 来实现上述的显示功能。假设该 Servlet 对应 Java 文件为 ServletJSP.java,其代
码如下:
/*** ServletJSP.java 文件 ***/
package ch7;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.*;
public class ServletJSP extends HttpServlet
{
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html; charset=gb2312");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("");
out.println("");
out.println("");
float x,y,z;
x=98.0f; //期考成绩
y=88.0f; //平时成绩
z=x*0.7f+y*0.3f; // 期评成绩,按七三开计算
out.println("");
out.println("");
out.println("");
52
out.println("");
out.println("姓名 ");
out.println("");
out.println("张无忌");
out.println("");
out.println("");
out.println("");
out.println("性别 ");
out.println("");
out.println("男");
out.println("");
out.println("");
out.println("");
out.println("成绩 ");
out.println("");
out.println(""+z+"");
out.println("");
out.println("");
out.println("");
out.println("");
out.println("");
out.close();
}
}
从 ServletJSP.java 文件中可以看出,所有的网页标记都要在 out.println()函数中打印出来,这非常不
利于设计网页的样式、颜色、尺寸等。而 ServletJSP.jsp 文件在这方面却有优势:它只是在需要 Java 代
码的地方才使用 Java 代码,否则都使用 HTML 代码。这样可以借助其他的网页设计工具,设计出漂亮
的 JSP 页面。所以,JSP 文件具有很大的可操作性。
进一步分析不难发现,Servlet 就是一个 Java 程序,适合于在“后台”做具有大计算量的工作,而
JSP 文件则适合于在“前台”做以信息显示为主、并带有少量计算性的工作。准确地讲,Servlet 适合用
于写事务层(而不是表示层)代码;而 JSP 则以嵌入一些 Java 片断为特征,为方便写表示层代码而设
计的。
实际上,SUN 公司首先推出的是 Servlet,而不是 JSP。但它输出 HTML 语句时是利用 out.println()
函数一句一句地输出, 使得页面设计变得非常麻烦。后来,SUN 公司才推出类似 ASP 的、 镶嵌型的 JSP,
即在 HML 代码中嵌入 JSP 元素而形成 JSP。
当然,Servlet 和 JSP 是不能相互取代的,它们各有优缺点。在应用时,要充分考虑各自的特点并
结合实际情况来选择,或者搭配使用等。
53
7.8 小结
本章首先介绍了 Servlet 的特点、功能和生命周期,以及 Servlet 的开发和部署方法;然后介绍了
Servlet API 提供常用的类和接口,为进一步的 Servlet 编程奠定基础;最后介绍了 servlet 上下文监听、
session 监听、request 监听技术。通过对本章的学习,读者应该掌握:
Servlet 的特点和功能等
Servlet API 提供的类和接口
servlet 上下文监听、session 监听、request 监听技术
Servlet 与 JSP 的关系
54