3. Java Web 开发基础
现在我们开始探讨动态 Web 应用程序的开发,即 c.a.d。这类应用程序中,发送给用户的页面 HTML 是由程序生成的。
3.1. 在Eclipse中创建Web项目
我们将使用 Eclipse/Tomcat 开发第一个 Web 应用程序。其操作步骤与不使用 Eclipse 创建 Web 应用程序时类似。启动 Eclipse 后,我们创建一个新项目:

将其定义为动态 Web 项目:

在创建向导的第一页中,我们指定项目名称为 [1] 及其位置为 [2]:

在向导的第二页中,我们接受默认值:

向导的最后一步要求我们定义应用程序的上下文 [3]:

向导通过后,Eclipse 会连接到 [http://java.sun.com] 网站,以检索某些需要缓存的文档,从而避免不必要的网络访问。随后将提示进行许可证授权:

点击“接受”。Eclipse 将创建 Web 项目。为了显示该项目,它使用了一个称为“视图”的环境,这与传统 Java 项目所用的环境不同:

与 Web 项目关联的视图是 J2EE。我们接受它以查看……结果如下:

对于简单的 Web 项目而言,J2EE 视图实际上过于复杂。在这种情况下,Java 视图就足够了。要切换到该视图,我们使用选项 [Window -> Open perspective -> Java]:

src:将包含应用程序类的 Java 代码,以及必须位于应用程序 Classpath 中的文件。
build/classes(未显示):将包含已编译类的 .class 文件,以及 src 目录中除 .java 文件以外的所有文件的副本。Web 应用程序通常会使用所谓的“资源”文件,这些文件必须位于应用程序的 Classpath 目录中,即 c.a.d。 当应用程序引用某个类时(无论是编译期间还是运行时),JVM 都会遍历所有文件夹。Eclipse 会确保 build/classes 文件夹成为 Web 应用程序的一部分。我们将“资源”文件放置在 src 文件夹中,Eclipse 会自动将其复制到 build/classes 中。
WebContent:将包含 Web 应用程序中无需放入应用程序 Classpath 中的资源。
WEB-INF/lib:将包含 Web 应用程序所需的 .jar 文件。
让我们查看配置应用程序 [personne] 的文件 [WEB-INF/web.xml] 的内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name> personne</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>
</web-app>
我们在第 2.3.4 节研究首页创建时已经遇到过这种类型的配置。该文件除了定义一系列首页外,别无他用。我们只保留第一个。[web.xml] 文件变为如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>personne</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
上述文件 XML 的内容必须遵循由 <web-app> 起始标签的 [xsi:schemaLocation] 属性指定的文件中定义的语法规则。此处该文件为 [http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd]。 这是一个可以直接通过浏览器请求的 XML 文件。如果浏览器版本足够新,它将能够显示 XML 文件:

Eclipse 将尝试使用 <web-app> 起始标签的 [xsi:schemaLocation] 属性中指定的 .xsd 文件,来验证 XML 文档的有效性。为此,它将进行网络访问。 如果您的计算机位于私有网络中,则需要向 Eclipse 指定用于连接外部网络的代理服务器,即 HTTP。这可以通过 [Window -> Preferences -> Internet] 选项实现:

若处于私有网络中,请勾选 (1)。在 (2) 中,输入支持 HTTP 代理的机器名称;在 (3) 中,输入该代理的监听端口。 最后在(4)中,列出无需通过代理连接的机器,即与当前工作机器位于同一私有网络中的机器。
现在我们将创建主页的 [index.html] 文件。
3.2. 创建主页
右键单击文件夹 [WebContent],然后选择选项 [New -> Other]:

选择类型 [HTML],并执行 [Next] ->

在上图中,我们在 (1) 或 (2) 处选择父文件夹 [WebContent],然后在 (3) 处指定要创建的文件名。完成此操作后,我们进入向导的下一页:

勾选 (1) 后,系统将生成一个由 (2) 预填充内容的 HTML 文件。若取消勾选 (1),则生成一个空白的 HTML 文件。 我们保留 (1) 的勾选状态,以便获得代码框架。通过 [Finish] 完成向导。随后将生成文件 [index.html]:

内容如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
我们将该文件修改如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Application personne</title>
</head>
<body>
Application personne active...
<br>
<br>
Vous êtes sur la page d'accueil
</body>
</html>
3.3. 首页测试
如果未显示,请使用选项 [Window - > Show View -> Other -> Servers] 调出视图 [Servers],然后右键单击 Tomcat 5.5 服务器:

上述选项 [Add and Remove Objects] 用于向 Tomcat 服务器添加/移除 Web 应用程序:

Eclipse 已识别的 Web 项目显示在 (1) 处。可通过 (2) 将其注册到 Tomcat 服务器。已注册到 Tomcat 服务器的 Web 应用程序显示在 (4) 处。可通过 (3) 取消注册。现在注册项目 [personne]:

然后通过 [Finish] 完成注册向导。[Servers] 视图显示,项目 [personne] 已注册到 Tomcat:

现在,启动 Tomcat 服务器:
![]() | ![]() |
启动网页浏览器:

然后访问网址 [http://localhost:8080/personne]。该网址是 Web 应用程序的根目录。 未请求任何文档。在这种情况下,将显示应用程序的首页。如果首页不存在,则会报告错误。这里首页是存在的。它就是我们之前创建的文件 [index.html]。得到的结果如下:

这符合预期。现在,我们使用 Eclipse 外部的浏览器访问同一 URL:

因此,Web 应用程序 [personne] 在 Eclipse 外部同样可见。
3.4. 创建表单 HTML
现在,我们在 [personne] 文件夹中创建一个静态文档 HTML:

创建时,请遵循第 33 页第 3.2 节所述的步骤。其内容如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Personne - formulaire</title>
</head>
<body>
<center>
<h2>Personne - formulaire</h2>
<hr>
<form action="" method="post">
<table>
<tr>
<td>Nom</td>
<td><input name="txtNom" value="" type="text" size="20"></td>
</tr>
<tr>
<td>Age</td>
<td><input name="txtAge" value="" type="text" size="3"></td>
</tr>
</table>
<table>
<tr>
<td><input type="submit" value="Envoyer"></td>
<td><input type="reset" value="Retablir"></td>
<td><input type="button" value="Effacer"></td>
</tr>
</table>
</form>
</center>
</body>
</html>
上述代码 HTML 对应下方的表单:

类型 HTML | 名称 | 代码 HTML | 角色 | |
<input type="text"> | txtNom | 第14行 | 输入姓名 | |
<input type="text"> | txtAge | 第18行 | 输入年龄 | |
<input type="submit"> | 第23行 | 将输入的值发送至服务器,URL 为 /personne1/main | ||
<input type="reset"> | 第24行 | 将页面恢复到浏览器最初接收时的状态 | ||
<input type="button"> | 第25行 | 用于清除输入字段 [1] 和 [2] 的内容 |
将文档保存到 <personne>/WebContent 文件夹中。如有需要,启动 Tomcat。使用浏览器访问 URL http://localhost:8080/personne/formulaire.html:

该基础应用程序的客户端/服务器架构如下:

Web 服务器位于用户与 Web 应用程序之间,此处未作展示。[formulaire.html] 是一个静态文档,会对每个客户端请求返回相同的内容。 Web编程旨在生成符合客户端请求的内容。该内容随后由程序生成。一种初步的解决方案是使用 JSP(Java Server Page)页面来替代静态文件 HTML。这就是我们接下来要探讨的内容。
3.5. 创建 JSP 页面
[ref1] 读取内容:第 1 章,第 2 章:2.2、2.2.1、2.2.2、2.2.3、2.2.4
先前的客户端/服务器架构转换如下:

JSP 页面是 HTML 页面的参数化形式。页面中的某些元素仅在运行时才获得其值。这些值由程序计算得出。 因此,这是一个动态页面:对该页面的连续请求可能会产生不同的响应。这里所说的响应,是指客户端浏览器显示的 HTML 页面。最终,浏览器收到的始终是一个 HTML 文档。 该 HTML 文档由 Web 服务器基于 JSP 页面生成。后者作为模板,其动态元素在生成 HTML 文档时被替换为当时的实际值。
要创建页面 JSP,我们右键单击文件夹 [WebContent],然后选择选项 [New -> Other]:

我们选择类型 [JSP],并执行 [Next] ->

在上图中,我们在 (1) 或 (2) 处选择父文件夹 [WebContent],然后在 (3) 处指定要创建的文件名。完成此操作后,我们进入向导的下一页:

勾选 (1) 后,系统将生成一个由 (2) 预填充内容的 JSP 文件。若取消勾选 (1),则生成一个空白的 JSP 文件。 我们保留 (1) 的勾选状态,以便获得代码框架。通过 [Finish] 完成向导。随后将生成文件 [formulaire.jsp]:

内容如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
第 1 行表明我们处理的是 JSP 页面。我们将上述文本转换如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
// 获取参数
String nom=request.getParameter("txtNom");
if(nom==null) nom="inconnu";
String age=request.getParameter("txtAge");
if(age==null) age="xxx";
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Personne - formulaire</title>
</head>
<body>
<center>
<h2>Personne - formulaire</h2>
<hr>
<form action="" method="post">
<table>
<tr>
<td>Nom</td>
<td><input name="txtNom" value="<%= nom %>" type="text" size="20"></td>
</tr>
<tr>
<td>Age</td>
<td><input name="txtAge" value="<%= age %>" type="text" size="3"></td>
</tr>
</table>
<table>
<tr>
<td><input type="submit" value="Envoyer"></td>
<td><input type="reset" value="Rétablir"></td>
<td><input type="button" value="Effacer"></td>
</tr>
</table>
</form>
</center>
</body>
</html>
通过引入Java代码,原本静态的文档现已变为动态文档。对于此类文档,我们将始终按照以下步骤操作:
- 在文档开头插入Java代码,以获取文档显示所需的参数。这些参数通常位于request对象中。该对象代表客户端的请求。该请求可能经过多个Servlet和JSP页面,这些组件可能对其进行了扩展。在此处,它将直接从浏览器传入。
- 后续是 HTML 代码。它通常仅通过 <%= 变量 %> 标签显示在 Java 代码中预先计算的变量。需注意此处等号(=)紧贴百分号(%),这是常见的错误原因。
前面的动态文档起什么作用?
- 第 6-9 行:它从查询中提取两个名为 [txtNom] 和 [txtAge] 的参数,并将它们的值赋给变量 [nom](第 6 行)和 [age] (第8行)。如果找不到这些参数,则为相关变量赋予默认值。
- 并在随后的代码 HTML 中显示这两个变量的值(第 25 行和第 29 行)。
让我们进行初步测试。如有需要,请启动 Tomcat,然后使用浏览器访问 URL http://localhost:8080/personne/formulaire.jsp:

文档 formulaire.jsp 是在未传递参数的情况下调用的。因此显示的是默认值。 现在请求 URL http://localhost:8080/personne/formulaire.jsp?txtNom=martin&txtAge=14:

这次,我们将formulaire.jsp文档中所需的参数txtNom和txtAge传递给了它。因此,它显示了这些参数。 我们知道,向 Web 文档传递参数有两种方法:GET 和 POST。在这两种情况下,传递的参数都会出现在预定义的 request 对象中。在此,它们是通过 GET 方法传递的。
3.6. 创建一个 Servlet
参考资料 [ref1]:第 1 章,第 2 章:2.1、2.1.1、2.1.2、2.3.1
在上一版本中,客户端的请求由页面 JSP 处理。首次调用该页面时,Web 服务器(此处为 Tomcat)会根据该页面生成一个 Java 类并进行编译。最终处理客户端请求的正是该编译结果。 由页面 JSP 生成的类是一个 Servlet,因为它实现了接口 [javax.Servlet]:

任何实现该接口的类均可处理客户端请求。现在我们构建这样一个类:ServletFormulaire。之前的客户端/服务器架构将转变为如下形式:

在基于页面 JSP 的架构中,发送给客户端的文档 HTML 是由 Web 服务器根据作为模板的页面 JSP 生成的。 在此情况下,发送给客户端的文档 HTML 将完全由 Servlet 生成。
3.6.1. 创建 Servlet
在 Eclipse 中,右键单击文件夹 [src],并选择创建类的选项:

然后定义要创建的类的属性:

在 (1) 中输入包名,在 (2) 中输入要创建的类名。该类必须继承自 (3) 中指定的类。无需手动输入该类的完整名称。通过 (4) 按钮可访问 Web 应用程序中 Classpath 当前包含的类:

在 (1) 中输入要查找的类名。在 (2) 中将显示 Classpath 中名称包含 (1) 中输入字符串的类。
在创建向导确认后,Web 项目 [personne] 将按以下方式进行修改:

已创建类 [ServletFormulaire],并包含以下代码框架:

上图显示,Eclipse 在声明类的行上提示了一个 [warning]。点击提示该 [warning] 的图标(灯泡):

点击 (1) 后,(2) 处会提供解决 [warning] 的方案。选择其中一个方案后,(3) 处会显示该方案将导致的代码修改。
Java 1.5 对 Java 语言进行了修改,此前版本中正确的代码现在可能会触发 [warnings] 提示。这些提示并非会阻碍类编译的错误,而是旨在提醒开发者注意代码中可改进之处。 当前出现的 [warning] 提示该类应包含版本号。该版本号用于对象的序列化/反序列化,即 c.a.d。 当内存中的 Java 对象 .class 需要转换为按顺序发送至写入流的位序列时,或反之,当内存中的 Java 对象 .class 需要从读取流中按顺序读取的位序列创建时。 这一切都与我们当前的关注点相去甚远。因此,我们将要求编译器忽略此警告,选择解决方案 [Add @SuppressWarnings ...]。代码随即变为如下形式:

不再有 [warning]。新增的这一行被称为“注解”,这是 Java 1.5 引入的概念。我们将在后续内容中完善这段代码。
3.6.2. Eclipse 项目的类路径
Java 应用程序的 Classpath 是指编译器在编译该应用程序时,或运行时所遍历的所有文件夹和 archives.jar。 这两个Classpath不一定完全相同,因为某些类仅在运行时有用,而在编译时则无用。 无论是 Java 编译器还是运行时环境,都提供了一个参数,用于指定要编译或运行的应用程序。Eclipse 会以对用户而言或多或少透明的方式,负责构建该参数并将其传递给运行时环境。
如何获取 Eclipse 项目中 Classpath 的内容?使用 [<projet> / Build Path / Configure Build Path] 选项:

随后将显示以下配置向导:

选项卡 (1) [Libraries] 用于定义属于应用程序 Classpath 的 .jar 归档文件列表。因此,当应用程序请求某个类时,JVM 会遍历这些归档文件。 按钮 [2] 和 [3] 用于向 Classpath 添加归档文件。 [2] 按钮用于指定 Eclipse 管理项目文件夹中的归档文件,而 [3] 按钮则用于指定计算机文件系统中的任意归档文件。
上图中显示了三个库(Libraries):
- [JRE System Library]:Eclipse Java 项目的基础库:

- [Tomcat v5.5 runtime]:由 Tomcat 服务器提供的库。它包含 Web 开发所需的类。该库包含在所有已关联 Tomcat 服务器的 Eclipse Web 项目中。

[servlet-api.jar] 归档文件中包含 [javax.servlet.http.HttpServlet] 类,该类是当前正在创建的 [ServletFormulaire] 类的父类。 正是因为该归档位于应用程序的 Classpath 中,才使得它能在下文提到的向导中作为父类被推荐。

如果不是这样,它就不会出现在 [2] 的建议列表中。 因此,若在该向导中需要引用父类,而该类未被列出,则说明要么输入的类名有误,要么包含该类的归档文件未位于应用程序的 Classpath 中。
- [Web App Libraries] 文件汇集了项目中 [WEB-INF/lib] 文件夹内的所有归档文件。此处该文件夹为空:

Eclipse 项目中的 Classpath 归档文件显示在项目资源管理器中。例如,对于 Web 项目 [personne]:

通过项目资源管理器,我们可以访问这些归档文件的内容:

由此可见,[servlet-api.jar] 归档中包含 [javax.servlet.http.HttpServlet] 类。
3.6.3. Servlet配置
[ref1] 阅读内容:第 2 章:2.3、2.3.1、2.3.2、2.3.3、2.3.4
文件 [WEB-INF/web.xml] 用于配置 Web 应用程序:

该项目([personne])的该文件当前内容如下(参见第 32 页):
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>personne</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
它仅指明了主页文件的存在(第 8 行)。我们将对其进行修改,以声明:
- [ServletFormulaire] Servlet
- 由该Servlet处理的URL
- Servlet 的初始化参数
我们“person”应用程序中的 web.xml 文件将如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>personne</display-name>
<servlet>
<servlet-name>formulairepersonne</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletFormulaire
</servlet-class>
<init-param>
<param-name>defaultNom</param-name>
<param-value>inconnu</param-value>
</init-param>
<init-param>
<param-name>defaultAge</param-name>
<param-value>XXX</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>formulairepersonne</servlet-name>
<url-pattern>/formulaire</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
该配置文件的主要要点如下:
- 第 7-24 行与 [ServletFormulaire] Servlet 相关
- 第 7-20 行:Servlet 的配置位于 <servlet> 和 </servlet> 标签之间。一个应用程序可以包含多个 Servlet,因此会有相应数量的 <servlet>...</servlet> 配置段。
- 第 8 行:<servlet-name> 标签为 Servlet 指定名称——名称可以任意设定
- 第 9-11 行:<servlet-class> 标签指定了与 Servlet 对应的类全名。Tomcat 将从 Web 项目 [personne] 的 Classpath 中查找该类。它将在 [build/classes] 中找到该类:
![]()
- 第 12-15 行:<init-param> 标签用于向 Servlet 传递配置参数。这些参数通常在 Servlet 的 init 方法中读取,因为 Servlet 的配置参数必须在其首次加载时就已知。
- 第 13-14 行:<param-name> 标签用于指定参数名称,<param-value> 标签用于指定其值。
- 第 12-15 行定义了参数 [defaultNom,"inconnu"],第 16-19 行定义了参数 [defaultAge,"XXX"]
- 第21-24行:<servlet-mapping>标签用于将一个Servlet(servlet-name)与URL模式(url-pattern)关联。 此处的映射规则很简单。它规定,每当 URL 采用 /formulaire 这种形式时,就必须使用名为 formulairepersonne 的 Servlet(c.a.d),即类 [istia.st.servlets.ServletFormulaire](第 8-11 行)。 因此,servlet [formulairepersonne] 仅接受一个 URL。
3.6.4. Servlet [ServletFormulaire] 的代码
Servlet [ServletFormulaire] 的代码如下:
仅从Servlet的代码来看,便可发现它比对应的JSP页面复杂得多。这是一个普遍现象:Servlet并不适合生成HTML代码。 JSP 页面才是为此设计的。我们稍后会再讨论这一点。下面我们来解释一下上述 Servlet 的几个要点:
- 当 Servlet 被首次调用时,会调用其 init 方法(第 20 行)。这是该方法被调用的唯一情况。
- 如果 Servlet 是由方法 HTTP 或 GET 调用的,则会调用方法 doGet(第 32 行)来处理客户端的请求。
- 如果 Servlet 是由方法 HTTP POST 调用的,则会调用方法 doPost(第 82 行)来处理客户端的请求。
此处的 init 方法用于从 [web.xml] 中获取名为“defaultNom”和“defaultAge”的初始化参数值。 在 Servlet 初始加载时执行的 init 方法是获取 [web.xml] 文件内容的合适位置。
- 第 22 行:获取 Web 项目的 [config] 配置。该对象反映了应用程序中 [WEB-INF/web.xml] 文件的内容。
- 第 23 行:从该配置中获取名为“defaultNom”的参数的 String 类型值。该参数的值将是一个人的姓名。 若该参数不存在,则将获取值 null。
- 第24-25行:如果名为“defaultNom”的参数不存在,则为变量[defaultNom]赋予默认值。
- 第 26-29 行:对名为“defaultAge”的参数也进行同样的处理。
方法 doPost 调用了方法 doGet。这意味着客户端既可以通过 POST,也可以通过 GET 发送其参数。
方法 doGet:
- 第 32 行:该方法接收两个参数 request 和 response。request 是一个对象,代表客户端的完整请求。其类型为 HttpServletRequest,这是一个接口。response 的类型为 HttpServletResponse,同样是一个接口。response 对象用于向客户端发送响应。
- request.getParameter("param") 用于从客户端请求中获取名为 param 的参数值。 第 36 行,获取参数 "txtNom" 的值;第 40 行,获取参数 "txtAge" 的值。 如果请求中不存在这些参数,则将 null 作为参数值。
- 第37-39行:如果请求中不包含参数“txtNom”,则将初始化方法init中设定的默认值“defaultNom”赋给变量“nom”。 第41-43行对年龄的处理方式相同。
- 第 45 行:response.setContentType(String) 用于设置 HTTP Content-type 标头的值。该标头向客户端指示其将接收的文档类型。text/html 类型表示 HTML 文档。
- 第 46 行:response.getWriter() 用于获取向客户端的写入流
- 第47-78行:将要发送给客户端的文档写入第46行获取的写入流中
编译此 Servlet 将在项目 [personne] 的 [build/classes] 文件夹中生成一个 .class 文件:

建议读者查阅 Java 关于 Servlet 的帮助文档。为此可以借助 Tomcat。在 Tomcat 5 的登录页面上,有一个链接 [Documentation]:

该链接将引导读者访问一个页面,建议您亲自探索。Servlet 文档的链接如下:

3.6.5. Servlet 测试
我们已准备好进行测试。如有需要,请启动 Tomcat 服务器。

然后使用浏览器访问 URL [http://localhost:8080/personne/formulaire]。此处我们请求的是 [/personne] 上下文中的 URL [/formulaire]。 该上下文中的文件 [web.xml] 表明,URL [/formulaire] 由名为 [formulairepersonne] 的 Servlet 处理。 在同一文件中,还指出该 Servlet 对应类为 [istia.st.servlets.ServletFormulaire]。因此,Tomcat 将委托该类处理客户端请求。如果该类尚未加载,则会进行加载。加载后,该类将保留在内存中以供未来请求使用。
使用Eclipse内置浏览器可获得以下结果:

我们得到了姓名和年龄的默认值,即 [web.xml] 文件中记录的值。现在请求 URL [http://localhost:8080/personne/formulaire?txtNom=tintin&txtAge=30]:

这次,我们获取到了请求中传递的参数。如果读者不理解这两个结果,建议重新阅读 [ServletFormulaire] Servlet 的代码。
3.6.6. 自动刷新Web应用程序上下文
启动 Tomcat:

然后将Servlet代码修改如下:
- 第 8 行已修改
保存新类。此保存操作将触发 Eclipse 自动重新编译 [ServletFormulaire] 类,Tomcat 将检测到此变化。 随后,它将重新加载 Web 应用程序上下文 [personne] 以反映这些更改。这会在视图 [console] 的日志中显示:

现在,让我们在不重启 Tomcat 的情况下访问 URL [http://localhost:8080/personne/formulaire]:

所做的修改已成功生效。
现在,我们将文件 [web.xml] 按以下方式修改:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>personne</display-name>
<servlet>
<servlet-name>formulairepersonne</servlet-name>
...
<init-param>
<param-name>defaultNom</param-name>
<param-value>INCONNU</param-value>
</init-param>
...
</servlet>
...
</web-app>
- 第 12 行已修改
完成上述操作后,保存新文件 [web.xml]。在 [console] 视图中,没有任何日志记录显示应用程序上下文已重新加载。在不重启 Tomcat 的情况下,请求 URL [http://localhost:8080/personne/formulaire]:

所做的修改未被采纳。重新启动 Tomcat [clic droit sur serveur -> Restart -> Start]:

然后再次请求 URL [http://localhost:8080/personne/formulaire]:

这次,在 [web.xml] 中所做的修改已可见。
因此,对 [web.xml] 的修改并不会触发应用程序的自动重载,从而应用新的配置文件。要强制重载 Web 应用程序,可以像之前那样重启 Tomcat,但这操作相当耗时。 更推荐使用 [manager] 工具来管理部署在 Tomcat 中的应用程序。要实现这一点,必须在 Eclipse 中按照第 2.5 节所示配置好 Tomcat。
首先,使用Eclipse的内置浏览器访问URL [http://localhost:8080],然后点击链接 [Tomcat Manager],具体操作如第2.5节末尾所述:

再打开第二个浏览器 [clic droit sur le navigateur -> New Editor]:
![]() | ![]() |
在此第二个浏览器中,访问网址 [http://localhost:8080/formulaire]:

将文件 [web.xml] 按以下方式修改并保存:
<!-- ServletFormulaire -->
<servlet>
<servlet-name>formulairepersonne</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletFormulaire
</servlet-class>
<init-param>
<param-name>defaultNom</param-name>
<param-value>YYY</param-value>
</init-param>
<init-param>
<param-name>defaultAge</param-name>
<param-value>XXX</param-value>
</init-param>
</servlet>
随后再次请求 URL [http://localhost:8080/formulaire]。我们可以发现修改并未生效。现在,回到第一个浏览器并重新加载应用程序 [personne]:

然后使用第二个浏览器再次请求 URL [http://localhost:8080/formulaire]:

[web.xml]的修改已生效。实际上,为了处理此类情况,建议在Tomcat中打开一个浏览器来访问[manager]应用程序。
3.7. Servlet 与 JSP 页面的协作
参考资料 [ref1]:第 2 章:2.3.7
让我们回到之前探讨的两种架构:

这两种架构均不尽如人意。它们都存在将两种技术混为一谈的弊端:一种是负责Web应用程序逻辑的Java编程技术,另一种是负责在浏览器中呈现信息的HTML编码技术。
- 基于 JSP 页面的 [1] 方案存在一个缺点,即在同一页面内混合了 HTML 代码和 Java 代码。 在处理的这个基础示例中,我们并未遇到此问题。但如果 [formulaire.jsp] 需要验证客户端请求参数 [txtNom, txtAge] 的有效性,我们就不得不将 Java 代码放入页面中。这很快就会变得难以管理。
- 基于Servlet的解决方案同样存在这个问题。尽管类中只有Java代码,但它必须生成一个文档。 同样地,除非 HTML 文档结构简单,否则其生成过程将变得复杂,且几乎无法维护。
我们将通过采用以下架构来避免 Java 与 HTML 技术的混合:

- 用户向 Servlet 发送请求。Servlet 处理该请求并构建页面 JSP [formulaire.jsp] 的动态参数值,这些参数将用于向客户端生成响应 HTML。 这些值共同构成了所谓的页面模板 JSP。
- 完成工作后,Servlet 将请求页面 JSP [formulaire.jsp] 向客户端生成响应 HTML。 同时,它还将向客户端提供页面 JSP 生成该响应所需的元素,这些元素构成了该页面的模型。
现在,我们将探讨这一新架构。
3.7.1. Servlet [ServletFormulaire2]
在上述架构中,该 Servlet 将命名为 [ServletFormulaire2]。它将与之前一样构建在 [personne] 项目中,后续的所有 Servlet 也将如此:

[ServletFormulaire2] 首先是通过在 Eclipse 中复制/粘贴 [ServletFormulaire] 获得的:
- 选中 [ServletFormulaire.java] -> 右键点击 -> 复制
- 选中 [istia.st.servlets.personne] -> 右键点击 -> 粘贴 -> 将其重命名为 [ServletFormulaire2.java]
随后,我们将 [ServletFormulaire2] 的代码修改如下:
仅 HTTP 的响应生成部分发生了变化(第 44-46 行):
- 第46行:响应的生成交由页面JSP formulaire2.jsp负责。 该页面尚未进行研究,将负责显示从客户端请求中获取的参数,即姓名(第35-38行)和年龄(第39-42行)。
- 这两个值被放入 [request] 请求的属性中,并关联到键上。请求的属性被作为字典进行管理。
- 第 44 行:姓名被放入查询中,并关联到“nom”键
- 第 45 行:将年龄放入查询中,并关联到键“age”
- 第 46 行:请求显示页面 JSP [formulaire2.jsp]。向该页面传递的参数为:
- 客户端的请求 [request],这将使页面 JSP 能够访问其刚刚由 Servlet 初始化的属性
- 响应 [response] 将使页面 JSP 能够向客户端生成响应 HTTP
编写完类 [ServletFormulaire2] 后,其编译后的代码将出现在 [build/classes] 中:

3.7.2. 页面 JSP [formulaire2.jsp]
页面 JSP formulaire2.jsp 是通过复制/粘贴页面 [formulaire.jsp] 获得的

随后按以下方式进行转换:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
// 获取显示所需的值
String nom=(String)request.getAttribute("nom");
String age=(String)request.getAttribute("age");
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Personne - formulaire2</title>
</head>
<body>
<center>
<h2>Personne - formulaire2</h2>
<hr>
<form action="" method="post">
<table>
<tr>
<td>Nom</td>
<td><input name="txtNom" value="<%= nom %>" type="text" size="20"></td>
</tr>
<tr>
<td>Age</td>
<td><input name="txtAge" value="<%= age %>" type="text" size="3"></td>
</tr>
</table>
<table>
<tr>
<td><input type="submit" value="Envoyer"></td>
<td><input type="reset" value="Rétablir"></td>
<td><input type="button" value="Effacer"></td>
</tr>
</table>
</form>
</center>
</body>
</html>
与 [formulaire.jsp] 相比,仅第 4-8 行发生了变化:
- 第6行:获取[request]请求中名为“nom”的属性值,该属性由Servlet [ServletFormulaire2]创建。
- 第7行:对“age”属性执行相同操作
3.7.3. 应用程序配置
配置文件 [web.xml] 修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>personne</display-name>
<!-- ServletFormulaire -->
<servlet>
<servlet-name>formulairepersonne</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletFormulaire
</servlet-class>
<init-param>
<param-name>defaultNom</param-name>
<param-value>inconnu</param-value>
</init-param>
<init-param>
<param-name>defaultAge</param-name>
<param-value>XXXX</param-value>
</init-param>
</servlet>
<!-- ServletFormulaire 2-->
<servlet>
<servlet-name>formulairepersonne2</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletFormulaire2
</servlet-class>
<init-param>
<param-name>defaultNom</param-name>
<param-value>inconnu</param-value>
</init-param>
<init-param>
<param-name>defaultAge</param-name>
<param-value>XXX</param-value>
</init-param>
</servlet>
<!-- 映射 ServletFormulaire -->
<servlet-mapping>
<servlet-name>formulairepersonne</servlet-name>
<url-pattern>/formulaire</url-pattern>
</servlet-mapping>
<!-- 映射 ServletFormulaire 2-->
<servlet-mapping>
<servlet-name>formulairepersonne2</servlet-name>
<url-pattern>/formulaire2</url-pattern>
</servlet-mapping>
<!-- 欢迎文件 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
我们保留了原有内容并添加了:
- 第 22-36 行:添加一个 <servlet> 部分,用于定义新的 ServletFormulaire2 Servlet
- 第 42-46 行:添加一个 <servlet-mapping> 部分,将其与 URL /formulaire2 关联
如有必要,请启动或重启 Tomcat 服务器。我们请求 URL
http://localhost:8080/personne/formulaire2?txtNom=milou&txtAge=10:

我们得到了与之前相同的结果,但应用程序的结构现在更加清晰:一个包含应用逻辑的Servlet,将向客户端发送响应的任务委托给页面JSP。今后我们将始终采用这种方式。



