JSP入门详细讲解

Java 专栏收录该内容
120 篇文章 0 订阅

组织中的所有人都能访问其某些方法,同时只让该组织中的某些享有特权的人访问其中一些方

法。同样是该Servlet 组件,可以针对另外一个环境而被配置成让每个人都能访问其所有方法,

或者仅让选定的少数人访问其所有方法。

J2EE 事务模型使得能够在部署时定义构成一个单一事务的方法之间的关系,以使一个事务

中的所有方法被处理成一个单一的单元。这是我们所希望的,因为一个事务是一系列步骤,这

些步骤要么全部完成,要么全部取消。例如,一个 Enterprise Beans 可能有一组方法,使我们可

以通过从第一个账户借出并存入第二个账户的方式而将钱从第一个账户转移到第二个账户。我

们希望全部的操作被作为一个单元对待,这样,如果在借出之后存入之前发生了故障,该借出

操作被取消。事务属性是在装配期间定义在一个组件上的。这使得能将来自多个应用组件的方

法归到一个事务中,这说明,我们可以轻易变更一个 J2EE 应用程序中的应用组件,并重新指定

事务属性,而不必改变代码或重新编译。在设计应用组件时,要记住,尽管 Enterprise Beans 有

一个可使应用组件的容器自动启动多步事务的机制,但是 Applet 和应用的客户容器可能并不支

持这一点。然而,Applet 和应用客户容器总是能够调用支持这一点的一个 Enterprise Beans。还

应当注意, JSP 页面和Servlet 没有被设计成是事务的,它们通常应当将事务工作交给一个

Enterprise Bean 来完成。然而,如果事务工作在一个 JSP 页面或Servlet 中是必须的,那么此种

工作也应当是非常有限的。

(7) 可重用应用组件

J2EE 组件(Applet 、应用的客户、Enterprise Beans 、JSP 页面及Servlet )都被打包成模块,

并以Java Archive (JAR )文件的形式交付。一个模块由相关的组件、相关的文件及描述如何配

置组件的配置描述文件组成。例如,在组装过程中,一个 HTML 页面和Servlet 被打包进一个模

块之中,该模块包含H T M L文件、Servlet 组件及相关的配置描述文件,并以一个 Web ARchive

(WAR )文件的形式交付,该WAR 文件是一个带.war 扩展名的标准JAR 文件。模块的使用使得

利用相同组件中的某些组件来组装不同的J2EE 应用程序成为可能。例如,一个J2EE 应用程序的

Web 版可能有一个Enterprise Beans 组件,还有一个JSP 页面组件。该Enterprise Beans 组件可以

与一个应用客户组件结合,以生成该应用程序的非 Web 版本。这不需要进行额外的编码,只是

一个装配和部署的问题。并且,可重用组件使得将应用开发和部署过程划分成由不同的角色来

完成成为可能,这样,不同的人或者公司就能完成封装和部署过程的不同部分。

2. J2EE 平台定义了如下角色:

(1) J2EE 产品提供商

设计并使J2EE 平台、API 和在J2EE 规范中定义的其他特征能被其他公司或人购得的公司。

(2) 应用组件提供商

创建用于J2EE 应用程序的Web 组件、Enterprise Beans 组件、Applet 或应用客户程序的公司

或个人。在装配过程中,应用组件文件、接口及类被打包进一个 JAR 文件中。

(3) 应用程序装配商

从组件提供商获得应用组件JAR 文件,并将它们组装成一个J2EE 应用的Enterprise Archive

(E A R)文件的公司或个人,这种文件是一个带 . e a r扩展名的标准文件。应用装配商提供与该应

用程序相关的整体信息,并使用验证工具来检验 EAR 文件的内容是正确的。组装和部署信息存

第一章 概 述计计5 下载下载

第一章 概 述

1.1   Java技术

J a v a是一种简单易用、完全面向对象、具有平台无关性且安全可靠的主要面向 I n t e r n e t的开

发工具。自从1 9 9 5年正式问世以来,J a v a的快速发展已经让整个We b世界发生了翻天覆地的变化。

随着Java Servlet的推出,J a v a在电子商务方面开始崭露头角,最新的 Java Server Page技术的推

出,更是让J a v a成为基于We b的应用程序的首选开发工具。

要学习J a v a技术中的Java Server Page,J a v a基础是必不可少的,本书将在第 2章为没有J a v a

基础的读者简单讲解J a v a的基础语法和Java Beans等, 它们是在学习J S P之前必须掌握的J a v a知识。

这里,先回顾一下J a v a的发展历程,然后讲解几个后面将要用到的重要概念。

1.1.1   Java技术的发展

J a v a技术是由美国 S u n公司倡导和推出的, J a v a技术包括J a v a语言和Java Media APIs、

Security APIs、Management APIs、Java Applet、Java RMI 、J a v a B e a n s、J a v a O S、Java Servlet、

J D B C、J N D I、Enterprise JavaBeans等,下面是J a v a技术的发展简述。

1 9 9 0年,S u n公司James Gosling领导的小组设计了一种平台独立的语言 O a k,主要用于为各

种家用电器编写程序。

1 9 9 5年1月,Oak 被改名为J a v a,1 9 9 5年5月2 3日,S u n公司在Sun World ‘9 5上正式发布

J a v a和H o t J a v a浏览器。

1 9 9 5年8月至1 2月,N e t s c a p e公司、O r a c l e公司、B o r l a n d公司、S G I公司、A d o b e公司、I B M

公司、AT & T公司、I n t e l公司获得J a v a许可证。

1 9 9 6年1月,S u n公司宣布成立新的业务部门─J a v a S o f t部,以开发、销售并支持基于 J a v a

技术的产品,由Alan Baratz任总裁。同时推出J a v a开发工具包JDK(Java Development Kit) 1.0,

为开发人员提供编制J a v a应用软件所需的工具。

1 9 9 6年2月,S u n发布J a v a芯片系列,包括p i c o J a v a、M i c r o J a v a和U l t r a J a v a,并推出J a v a数据

库连接JDBC (Java Database Connectivity)。

1 9 9 6年3月,S u n公司推出Java Wo r k S h o p。

1 9 9 6年4月,M a c r o s o f t公司、S C O公司、苹果电脑公司( A p p l e )、N E C公司等获得J a v a许可证。

S u n公司宣布苹果电脑、H P、日立、I B M、微软、N o v e l l、S G I、S C O、Ta m d e m等公司将把J a v a

平台嵌入到其操作系统中。

第一部分 JSP    入 门1 9 9 6年5月,H P公司、S y b a s e公司获得J a v a许可证。北方电讯公司宣布把 J a v a技术和J a v a微

处理器应用到其下一代电话机中的计划。 5月2 9日,S u n公司在旧金山举行第一届 J a v a O n e世界

J a v a开发者大会,业界人士踊跃参加。S u n公司在大会上推出一系列J a v a平台新技术。

1 9 9 6年8月,Java Wo r k S h o p成为S u n通过互联网提供的第一个产品。

1 9 9 6年9月,A d d i s o n - We s l e y和S u n推出J a v a虚拟机规范和J a v a类库。

1 9 9 6年1 0月,德州仪器等公司获得J a v a许可证。S u n提前完成J a v a B e a n s规范并发布。发布第

一个Java JIT(Just-In-Ti m e )编译器,并打算在Java Wo r k S h o p和S o l a r i s操作系统中加入J I T。1 0月

2 9日,S u n发布J a v a企业计算技术,包括J a v a S t a t i o n网络计算机、6 5家公司发布的 8 5个J a v a产品

和应用、7个新的J a v a培训课程和J a v a咨询服务、基于J a v a的S o l s t i c e互联网邮件软件、新的J a v a

开发者支持服务、演示 HotJava Vi e w s、Java Tu t o r、完成Java Card API等。S u n宣布完成J a v a

Card API规范,这是智能卡使用的第一个开放 A P I。Java Card规范将把J a v a能力赋予全世界的亿

万张智能卡。

1 9 9 6年11月,I B M公司获得J a v a O S和H o t J a v a许可证。N o v e l l公司获得Java Wo r k S h o p许可证。

S u n和I B M宣布双方就提供J a v a化的商业解决方案达成一项广泛协议, I B M同意建立第一个 J a v a

检验中心。

1 9 9 6年1 2月,X e r o x等公司获得J a v a或J a v a O S许可证。S u n发布JDK 1.1、J a v a商贸工具包、

J a v a B e a n s开发包及一系列Java APIs。推出一个新的Java Server产品系列,其中包括 Java We b

S e r v e r、Java NC Server和 Java Server To o l k i t。S u n发布1 0 0 %纯J a v a计划,得到百家公司的支持。

1 9 9 7年1月,S A S等公司获得J a v a许可证。S u n交付完善的J a v a B e a n s开发包,这是在确定其

规范后不到8个月内完成的。

1 9 9 7年2月,S u n和A R M公司宣布同意使J a v a O S能运行在A R M公司的R I S C处理器架构上。

I n f o r m i x公司宣布在其Universal Server和其他数据库产品上支持 JDK 1.1。N e t s c a p e公司宣布其

Netscape Communicator支持所有J a v a化的应用软件和核心A P I s。

1 9 9 7年3月,H P公司获得Java Wo r k S h o p许可证,用于H P - U X操作系统。西门子A G公司等获

得J a v a许可证。日立半导体公司、 I n f o r m i x公司等获得 J a v a O S许可证。N o v e l l公司获得J a v a

S t u d i o许可证。S u n发售JavaOS 1.0操作系统,这是一种在微处理器上运行J a v a环境的最小、最快

的方法,提供给S u n的J a v a O S许可证持有者使用。S u n发售HotJava Browser 1.0,这是一种J a v a浏

览环境,可以方便地按剪裁来编制专用的信息应用软件,如客户自助台和打上公司牌号的网络

应用软件。S u n推出JDK 1.1.1。

1 9 9 9年6月,Sun 发布JDK 1.3和Java Web Server 2.0。

1.1.2    JavaBeans

什么是J a v a B e a n s?J a v a B e a n s就是J a v a的可重用组件技术。A S P通过C O M来扩充复杂的功能,

如文件上载、发送e m a i l以及将业务处理或复杂计算分离出来成为独立可重复利用的模块。 J S P通

过J a v a B e a n实现了同样的功能扩充。JSP 对于在 Web 应用中集成 JavaBean 组件提供了完善的支

持。这种支持不仅能缩短开发时间(可以直接利用经测试和可信任的已有组件,避免了重复开

发) ,也为 JSP 应用带来了更多的可伸缩性。JavaBean 组件可以用来执行复杂的计算任务,或负

2计计第一部分 JSP    入 门

下载责与数据库的交互以及数据提取等。

在实际的J S P开发过程中,读者将会发现,和传统的 A S P或P H P页面相比,J S P页面将会是非

常简洁的,由于J a v a B e a n s开发起来简单,又可以利用 J a v a语言的强大功能,许多动态页面处理

过程实际上被封装到了J a v a B e a n s中。

1.1.3   JDBC

J D B C是用于执行S Q L语句的J a v a应用程序接口,由一组用 J a v a语言编写的类与接口组成,

在J S P中将使用J D B C来访问数据库。J D B C是一种规范,它让各数据库厂商为 J a v a程序员提供标

准的数据库访问类和接口, 这样就使得独立于D B M S的J a v a应用程序的开发工具和产品成为可能。

一般的J a v a开发工具都带有J D B C - O D B C桥驱动程序,这样,只要是能够使用 O D B C访问的数据

库系统,也就能够使用J D B C访问了。有趣的是,不同于 O D B C是Open Database Connectivity的

简称,J D B C并不是Java Database Connecivity的简称,而是S U N的注册商标,至少官方说法是这

样的。

1.1.4   J2EE

电子商务和信息技术的快速发展以及对它们的需求给应用程序开发人员带来了新的压力。

必须以比以前更少的金钱、更少的资源来更快地设计、开发企业应用程序。

为了降低成本,并加快企业应用程序的设计和开发, J2EE 平台提供了一个基于组件的方法,

来设计、开发、装配及部署企业应用程序。 J2EE 平台提供了多层的分布式的应用模型、组件再

用、一致化的安全模型以及灵活的事务控制。您不仅可以用比以前更快的速度向市场推出创造

性的客户解决方案,而且您的平台独立的、基于组件的 J2EE 解决方案不会被束缚在任何一个厂

商的产品和API 上。

1. J2EE 规范定义了以下种类的组件

• 应用客户组件。

• Enterprise JavaBeans 组件。

• S e r v l e t及JavaServer Pages(JSP 页面)组件(也被称作Web 组件) 。

• Applet。

一个多层的分布式的应用模型意味着应用逻辑被根据功能划分成组件,并且可以在同一个

服务器或不同的服务器上安装组成 J2EE 应用的这些不同的组件。一个应用组件应被安装在什么

地方,取决于该应用组件属于该多层的J2EE 环境中的哪一层。这些层是客户层、We b层 、 业务

层及企业信息系统层(EIS )等。

(1) 客户层

J2EE 应用可以是基于Web 的,也可以是不基于Web 的。在一个基于Web 的J2EE 应用中,

用户的浏览器在客户层中运行,并从一个 We b服务器下载Web 层中的静态HTML 页面或由J S P

或Servlet 生成的动态HTML 页面。在一个不基于Web 的J2EE 应用程序中,一个独立客户程序不

运行在一个HTML 页面中,而是运行在其他一些基于网络的系统(比如手持设备或汽车电话)

中,Applet 程序,在客户层中运行,并在不经过 Web 层的情况下访问Enterprise Beans。这个不

第一章 概 述计计3 下载基于Web 的客户层可能也包括一个JavaBeans 类来管理用户输入,并将该输入发送到在企业层中

运行的Enterprise Beans类来进行处理。根据J2EE 规范,JavaBeans 类不被视为组件。

为J2EE 平台编写的JavaBeans 类有实例变量和用于访问实例变量中的数据的“ get 和set 方

法” 。以这种方式使用的 JavaBeans 类在设计和实现上通常都是简单的,但是它们必须符合

JavaBeans 规范中列出的命名和设计约定。

(2) Web 层

J2EE Web 组件可以由JSP 页面、基于Web 的A p p l e t以及显示HTML  页面的S e r v l e t组成。调

用S e r v l e t或者JSP  页面的HTML 页面在应用程序组装时与Web 组件打包在一起。就像客户层一

样,Web 层可能包括一个 JavaBeans 类来管理用户输入,并将输入发送到在业务层中运行的

Enterprise Beans 类来进行处理。运行在客户层的Web 组件依赖容器来支持诸如客户请求和响应

及Enterprise Bean 查询等。

(3) 业务层

作为解决或满足某个特定业务领域(比如银行、零售或金融业)需要的逻辑的业务代码由

运行在业务层的Enterprise Beans 来执行。一个Enterprise Bean 从客户程序处接收数据,对数据

进行处理(如果需要) ,再将数据发送到企业信息系统层存储起来。一个 Enterprise Beans 还从存

储中检索数据,并将数据送回客户程序。运行在业务层的 Enterprise Beans 依赖于容器来为诸如

事务、生命期、状态管理、多线程及资源存储池等提供通常都非常复杂的系统级代码。业务层

经常被称作Enterprise JavaBeans (EJB )层。业务层和Web 层一起构成了3 层J 2 E E应用的中间

层,而其他两层是客户层和企业信息系统层。

(4) 企业信息系统层

企业信息系统层运行企业信息系统软件,这层包括企业基础设施系统,例如企业资源计划

(E R P) 、大型机事务处理(mainframe transactionprocessing ) 、数据库系统及其他遗留信息系统

(legacy informationsystems ) 。J2EE 应用组件因为某种原因( 例如访问数据库) 可能需要访问企

业信息系统。J2EE 平台的未来版本将支持Connector 架构,该架构是将J2EE 平台连接到企业信

息系统上的一个标准A P I。

(5) 查询服务

因为一个J2EE 应用程序的组件是单独运行的,并且往往在不同的设备上运行,因此,需要

一种能让客户层和 Web 层代码查询并引用其他代码和资源的方法。客户层和 Web 层代码使用

Java 命名和目录接口(JNDI )来查询用户定义的对象(例如Enterprise Beans ) 、环境条目(例

如一个数据库驱动器的位置) 、企业信息系统层中用于查找资源的 JDBC DataSource对象,以及

消息连接。

(6) 安全和事务管理

诸如安全和事务管理这样的应用行为可以在部署时在 Web 和Enterprise Beans 组件上进行配

置。这个特征将应用逻辑从可能随装配而变化的配置设定中分开了。

J2EE 安全模型允许配置一个Web 或Enterprise Beans 组件,使系统资源只能由授权的用户访

问。例如,一个Web 组件可以被配置成提示输入用户名和密码。一个 Enterprise Beans 组件可以

被配置成只让特定团体中的成员调用其某些方法。或者,一个 Servlet 组件可以被配置成让某个

4计计第一部分 JSP    入 门

下载储在一个基于文本的配置描述文件中,此种文件使用 XML  标记来标记该文本。应用装配商可以

使用一个能通过交互式选择来正确添加XML 标记的装配和配置工具来编辑该配置描述文件。

(4) 部署商

部署(d e p l e y)J2EE 应用程序的公司或个人。其职责包括设定事务控制、安全属性,并根

据应用组件提供商提供的指示来标明一个 Enterprise Bean 是自己处理自身的存储,还是由一个

容器来处理等。部署涉及配置和安装。在配置过程中,部署商遵循应用组件提供商提供的指示

来解决外部依赖问题,定义安全设定,以及分配事务属性。在安装过程中,部署商将应用组件

安装到服务器上,并生成容器特定的类和接口。

(5) 系统管理员

配置并管理运行J2EE 应用程序的计算环境和网络基础设施,并监督运行环境的人员。

(6) 工具提供商

生产被组件提供商、装配商及部署商使用的用于进行开发、组装和打包的工具的公司或个

人。

(7) 设计用户界面和引擎

在为J2EE 应用程序设计用户界面和后端引擎时,需要决定让该程序是基于 We b,还是不基

于We b。在做出这个决定时,我们可能希望考虑平台配置、下载速度、安全、网络流量和网络服

务。

例如,包含有用户界面并且经常被大量用户访问的一个 Applet 可能需要花很长的时间才能

被下载下来,这让用户沮丧。然而,如果知道该 A p p l e t要运行在一个公司的内部网内的受控环境

中,那么,在这种情况下,该 Applet 将拥有一个完全可接受的下载速度。另一个考虑是,繁重

的处理应当在哪里执行。例如,如果客户程序在一个蜂窝电话或呼机中执行,服务器应当完成

尽量多的计算和数据处理,而客户程序只应显示结果就可以了。然而,设计在一个强大的台式

机平台上运行的大型财务分析系统则应当在客户机上完成其复杂计算。应用的客户程序和 A p p l e t

用户界面通常都是用Swing API 创建的,该API 可从标准版J a v a 2平台中获得。Swing API 提供了

一整套GUI 组件(表格、树形结构、按钮等) ,这些组件可以被用来实现一种比用一个典型的

HTML 页面所能实现的更为交互的体验。 Swing 也支持HTML 文本组件,这个组件可以被用来

显示来自一个服务器的响应。客户程序可以直接访问 Enterprise Beans 层或企业信息系统层。但

应谨慎实现这种程序。绕过EJB 层的程序可以使用JDBC API 来访问一个关系型数据库,但应被

限制于对数据库表格进行维护等管理任务上。

(8) 设计基于Web 的应用程序

基于Web 的应用程序是基于浏览器的,并且,如果它们运行在 I n t e r n e t上,就可能被全世界

的人访问。当设计一个基于 Web 的应用程序时,不仅需要决定用什么来处理内容和应用逻辑

(HTML 、XML  、JSP 页面及S e r v l e t) ,而且还应当考虑使该应用程序国际化。一个国际化的基

于Web 的应用程序向用户提供了选择一种语言,然后根据该选定语言加载应用的正文的方式。

对被支持的每种语言而言,应用正文都被存储在一个外部文件中,并且与另外一个文件的关键

词相对应。应用代码使用这些关键词及选定的语言来加载正确的文本。国际化 API 还提供类来

根据选定的语言格式化日期和金钱。一旦制订了使应用程序国际化的细节,就可以决定用什么

6计计第一部分 JSP    入 门

下载来实现它了。总的来说,一个基于 Web 的应用程序使用HTML 来显示数据; 用X M L来定义数据

以使其可被另一个程序读取并处理 ; 使用JSP  页面或S e r v l e t来管理用户与业务层或存储层之间的

数据流。

可以在J2EE 平台上实现的基于Web 的应用程序有四种。从简单到复杂排列,它们是 :

• B基本H T M L。

• B带基本JSP 页面或Servlet 的H T M L。

• B带Java Beans 类的J S P页面。

• B将应用逻辑根据功能划分成区域的高度结构化的应用。

当设计一个基于Web 的应用程序时,需要决定用什么来建立它。如果是从建立一个简单的

应用程序开始着手,并且认为以后会给该应用程序添加功能,那么,设计就应当适应今后发展

的需要。

(9) 模型、视图和控制器架构

在基于组件的J2EE 平台充分内置了灵活性的情况下,剩下的问题可能是如何组织应用程序

以实现简单高效的应用程序升级和维护,以及如何让不懂程序代码的人员避开程序数据。答案

就在模型、视图和控制器架构( MVC )的使用之中。MVC  这样的架构是一个描述重现的问题

及其解决方案的设计范式,但每次问题重现时,解决方案都不会完全相同。

MVC  设计范式包括三种对象:模型( model )提供应用业务逻辑(Enterprise Beans 类);

视图(view )则是其在屏幕上的显示( HTML 页面、JSP 页面、Swing GUI ); 控制器则是

Servlet 、JavaBeans 或Session Beans 类,它用于管理用户与视图发生的交互。我们可以将控制

器想像成处在视图和数据之间,对视图如何与模型交互进行管理。通过使视图完全独立于控制

器和模型,就可以轻松替换前端客户程序。并且,通过将控制器和模型代码保持在视图之外,

那些不理解这些代码的人员就不能改变他们不应改变的东西。

将控制器和模型分开就可以在不影响模型的情况下改变控制器,也可以在不影响控制器的

情况下改变模型。例如,如果应用的前端是一个 HTML 页面,HTML 专家就可以更新它。如果

使用一个J S P页面,将控制器的代码放到一个 JavaBeans 或SessionBeans 类中,或使用动作标记

(action tags ) ,这样,JSP 页面就仅包含JSP 代码了。

本书将在第二部分中讲解如何使用J 2 E E来建立企业级的We b应用。

1.1.5   EJB

E J B就是前面说的Enterprise JavaBeans。E J B上层的分布式应用程序是基于对象组件模型的,

低层的事务服务使用了A P I技术。E J B技术简化了用J AVA语言编写的企业应用系统的开发、配置

和执行。E J B的体系结构规范由Sun Microsystems公司制定。

E J B技术定义了一组可重用的组件:Enterprise Beans。可以利用这些组件像搭积木一样你的

建立分布式应用程序。当你把代码写好之后,这些组件就被组合到特定的文件中去。每个文件

有一个或多个Enterprise Beans,在加上一些配置参数。最后,这些 Enterprise Beans被配置到一

个装了E J B容器的平台上。客户能够通过这些 B e a n s的Ho m e接口,定位到某个Be a n s,并产生这

个Be a n s的一个实例。这样,客户就能够调用B e a n s的应用方法和远程接口。

第一章 概 述计计7 下载E J B服务器作为容器和低层平台的桥梁,管理着 E J B容器和函数。它向E J B容器提供了访问

系统服务的能力。例如:数据库的管理和事务的管理,或者其他的 E n t e r p r i s e的应用服务器。

J2EE 应用程序中的Enterprise Beans

当编写管理特定业务功能(比如追踪雇员资料或进行复杂财务计算)的 J2EE 应用程序时,

请将完成这些任务的业务逻辑放置在 EJB 层的Enterprise Beans 中。通过这种方式,就可以使代

码集中在解决手边的业务问题,而利用 Enterprise Beans 容器来支持低层服务,比如状态管理、

事务管理、线程管理、远程数据访问和安全等。将业务逻辑与低层系统逻辑分开意味着容器可

以在运行时创建和管理Enterprise Beans。按照规范编写的任何Enterprise Beans,都可以根据其

在一个特定的J2EE 应用程序中将被如何使用来对其事务管理或安全属性进行配置,并可以被部

署到任何一个与规范兼容的容器中。可重用组件使不必改变和重新编译 Enterprise Beans 代码成

为可能。一个Enterprise Beans 由接口和类组成。客户程序通过Enterprise Beans的Home 和远程

接口来访问Enterprise Beans 的方法。Home 接口提供了创建、删除和定位Enterprise Beans 的方

法,而远程接口则提供了业务方法。在部署时,容器由这些接口来创建类,使客户能够创建、

删除、定位或调用位于Enterprise Beans 上的业务方法。Enterprise Beans 类提供了业务方法、创

建方法和查询方法的实现。如果 Enterprise Beans 管理它自己的持久性的话,还为其生命期方法

提供了实现。有两种Enterprise Beans :Entity Beans 和Session Beans。

一个Session Beans 代表与客户程序的一个短暂会话,而且可能执行数据库读写操作。一个

Session Beans 可能会自己调用J D B C,或者它可能使用Entity Beans来完成此种调用。在后面这

种情况下,这个Session Beans 是该Entity Beans 的客户。一个Session Beans 的域包含会话状态,

而且是短暂的。如果服务器或者客户程序崩溃,该 Session Beans 就丢失了。这种模式通常被用

于像PL/SQL 这样的数据库程序设计语言上。

一个Entity Beans 代表一个数据库中的数据及作用于该数据的方法。在一个关系型数据库中

的雇员信息表中,每一行都有一个Beans 来代表。Entity Beans 是事务的,并且是长寿命的。只

要数据留在数据库中,Entity Beans 就存在。这个模式可以被很容易地用于关系型数据库,而不

仅仅限于对象数据库。

Session Beans 可以是有状态的,也可以是无状态的。一个有状态的 Session Beans 包含代表

客户程序的会话状态。该会话状态是该 Session Beans实例的域值加上这些域值所引用到的所有

对象。有状态Session Beans 并不代表在一个持久数据存储中的数据,但是,它可以代表客户程

序访问和更新数据。无状态Session Beans 没有用于某个特定客户程序的任何状态信息。它们通

常被用于提供不保持任何特定状态的服务器端行为。无状态 sessionBeans 要求更少的系统资源。

一个提供一种一般服务,或用于表示被存储的数据的一个被共享的视图的业务对象是无状态

Session Bean的一个例子。

因为Enterprise Beans 占用可观的系统资源和带宽,你可能希望将某些业务对象构造成数据

访问对象或值对象。数据访问对象完成诸如代表客户程序访问数据库等工作。值对象用于代表

容纳数据字段并提供简单的“ get 和set ”方法来访问这些数据的一个结构。另外,可以将程序

构造成使用Enterprise Bean 在客户和EJB 层的其他部分之间承担通信的任务。

对于一个使用容器管理的持久性来访问关系型数据库的 Enterprise Beans,并不要求在B e a n s

8计计第一部分 JSP    入 门

下载的代码中使用任何JDBC 2.0 API 来进行数据库访问,因为容器完成了这些工作。然而,如果使

用Beans 管理的持久性,或者要访问一个非关系型数据库的企业信息系统,那么就必须在 B e a n s

中提供相应的代码来完成这些工作。在 Enterprise Beans 使用Beans 管理的持久性来访问一个数

据库的情况下,必须使用JDBC 2.0 API 代码来实现该Enterprise Beans 的生命期方法,以便处理

数据的加载和存储,以及运行时在系统和持久数据存储之间维持数据的一致性。

一个使用Beans 管理的持久性的Enterprise Beans,或一个需要访问企业信息系统的Web 组件

必须提供合适的代码。这些代码可能是用于进行数据库访问的 JDBC 2.0 API ;或是用于访问一

个特定企业信息系统的企业信息系统API ;或是用于抽象企业信息系统API 的复杂性和低层细节

的一个访问对象,或是用于访问企业信息系统资源的一个连接对象。

尽管Web 层使用HTTP 或HTTPS 来在各层之间传输数据,但是 EJB 层使用的是R M I - I I O P。

RMI-IIOP 是一个完整的分布式计算协议,能让任何访问 Enterprise Bean 的客户层程序或Web 层

程序直接访问EJB 层的服务。这些服务包括用于查找和引用 Enterprise Beans 的J N D I,发送和接

收异步消息的Java Message Service (JMS ) ,以及用于关系型数据库访问的J D B C。

1.1.6   Java Servlet

Java Servlet是J S P技术的基础,而且大型的We b应用程序的开发需要Java Servlet和J S P配合

才能完成,这里简单介绍S e r v l e t的相关知识,S e r v l e t的开发将在第二部分讲述。

S e r v l e t这个名称大概源于A p p l e t,现在国内的翻译方式很多,本书为了避免误会,决定直接

采用S e r v l e t而不做任何翻译,读者如果愿意,可以称之为“小服务程序” 。S e r v l e t其实和传统的

C G I程序和I S A P I、N S A P I等We b程序开发工具的作用是相同的,在使用 Java Servlet以后,用户

不必再使用效率低下的C G I方式,也不必使用只能在某个固定 We b服务器平台运行的A P I方式来

动态生成We b页面。许多We b服务器都支持S e r v l e t,即使不直接支持S e r v l e t的We b服务器也可以

通过附加的应用服务器和模块来支持 S e r v l e t。得益于J a v a的跨平台的特性,S e r v l e t也是平台无关

的,实际上,只要符合 Java Servlet规范,S e r v l e t是完全平台无关且是We b服务器无关的。由于

Java Servlet内部是以线程方式提供服务,不必对于每个请求都启动一个进程,并且利用多线程

机制可以同时为多个请求服务,因此Java Servlet效率非常高。

但Java Servlet也不是没有缺点,和传统的C G I、I S A P I、N S A P I方式相同,Java Servlet是利

用输出H T M L语句来实现动态网页的,如果用 Java Servlet来开发整个网站,动态部分和静态页

面的整合过程简直就是一场恶梦。这就是为什么 S U N还要推出Java Server Pages的原因。

1.2   JSP技术

前面说过,Java Servlet的最大缺点就在于没有把网站的逻辑和页面的输出分开,导致整个

S e r v l e t代码混乱不堪。为了解决Java Servlet的这种缺点,S U N推出了Java Server Pages—J S P。

1.2.1   JSP技术概述

按照脚本语言是服务于某一个子系统的语言这种论述, J S P应当被看作是一种脚本语言,然

而,作为一种脚本语言,J S P又显得过于强大了,在J S P中几乎可以使用全部的J a v a类。

第一章 概 述计计9 下载作为一种基于文本的、以显示为中心的开发技术,J S P提供了Java Servlet 的所有好处,并且,

当与一个JavaBeans 类结合在一起时,提供了一种使内容和显示逻辑分开的简单方式。分开内容

和显示逻辑的好处是,更新页面外观的人员不必懂得 Java 代码,而更新J a v a B e a n s类的人员也不

必是设计网页的行家里手,就可以用带JavaBeans 类的JSP 页面来定义Web 模板,以建立一个由

具有相似的外观的页面组成的网站。JavaBeans 类完成数据提供,这样在模板中就没有Java 代码,

这意味着这些模板可以由一个 HTML 编写人员来维护。当然,也可以利用 Java Servlet来控制网

站的逻辑,通过Java Servlet调用J S P文件的方式来将网站的逻辑和内容分离。本章我们后面将对

这种分离网站的逻辑和内容的设计方法做一些更深入的描述。

在选择使用一个Java Servlet,还是一个JSP  页面时,要记住的是,Java Servlet 是一个程序

设计工具,它最适用于不需要频繁修改的低级应用功能 ; 而J S P页面则通过以显示为中心的描述

性的方法将动态内容和逻辑结合在一起。对于使用一个 JSP  页面的简单的基于Web 的应用程序,

可以使用定制标记或者S c r i p t l e t,而不是使用JavaBeans 类来将内容与应用逻辑结合起来。定制

标记被打包到一个标记库中,并被引入到一个 JSP  页面中。S c r i p t l e t是直接嵌入在JSP  页面中的

很小的J a v a代码段。

一般来说,在实际的 J S P引擎中,J S P页面在执行时是编译式,而不是解释式的。解释式的

动态网页开发工具如A S P、P H P 3等由于速度等原因已经满足不了当前大型电子商务应用的需要

了,传统的开发技术都在向编译执行的方式改变,如 A S P→A S P +;P H P 3→P H P 4。而尽管J S P的

规范书中并没有要求实际的J S P引擎要使用编译式的执行方式,但估计一般是不会使用解释的方

式来执行J S P页面的。通常说来,J S P页面一般是翻译为S e r v l e t的J a v a源文件,再经过J a v a编译器

编译为S e r v l e t的c l a s s文件。为什么要编译为S e r v l e t呢?据说是为了让原先的S e r v l e t引擎可以直接

服务于J S P,而J S P引擎就仅仅需要将J S P转译为S e r v l e t就可以了。这里要注意的是: J S P规范书

中并没有规定如何将 J S P页面转译为S e r v l e t,因此,不同的J S P引擎转译的结果也是不一样的。

在J S P文件转译为S e r v l e t以后,每次客户机(通常是用户的We b浏览器)向服务器请求这一个J S P

文件的时候,服务器将检查自上次编译后 J S P文件是否有改变,如果没有改变,就直接执行

S e r v l e t,而不用再重新编译,其效率是相当高的。一般来说, J S P文件的编译是在第一个用户访

问到这个J S P页面时发生,而这第一个用户通常是开发人员自己,这样,正式放在服务器上让用

户访问的J S P文件一般都已经有了对应的编译好的 S e r v l e t了。许多服务器都有设置,可以使 J S P

文件在第一个用户访问之前就预先编译好,这样看来,效率就更高了。后面在第 4章中,将展示

一个简单的J S P文件对应的S e r v l e t。

在J S P规范书中,并没有明确要求J S P中的程序代码部分(称为S c r i p t l e t)一定要用J a v a来写,

实际上,有一些J S P引擎就是采用的其他脚本语言,如:E M A C - S c r i p t、We b L等等,但实际上这几

种脚本语言也是构建在J a v a上面,编译为S e r v l e t来实现的。按照J S P规范书,完全和J a v a没有任何关

系的S c r i p t l e t也是可以的,不过,由于J S P的强大功能主要在于能和J a v a B e a n s、Enterprise JavaBeans

一起工作,所以即使是Scriptlet部分不使用Java,编译成的执行代码也应该是与Java相关的。

1.2.2   JSP的优势及与其他We b开发工具的比较

和传统的C G I相比较,J S P有相当的优势。首先,在速度上,传统的 C G I程序需要使用系统

10计计第一部分 JSP    入 门

下载的标准输入输出设备来实现动态网页的生成,而 J S P是直接和服务器相关联的。而且对于 C G I来

说,每一个访问就需要新增加一个进程来处理,进程不断地建立和销毁对于作为 We b服务器的计

算机将是不小的负担。其次, J S P是专门为We b开发而设计的,其目的是为了建立基于 We b的应

用程序,包含了一整套的规范和工具。使用 J S P技术可以很方便地将一大堆 J S P页面组合成为一

个We b应用程序。

和I S PA I和N S A P I相比较,J S P的开发速度要快得多,开发难度也要小得多,在编译为 J a v a

S e r v l e t以后,配合目前最新的J I T(Just In Ti m e)的J a v a解释器,其执行速度也慢不了多少。而

且,I S A P I和N S A P I这种和We b服务器过于紧密结合的技术在使用时的一旦出现错误,很容易使

We b服务器崩溃,而J S P就没有这个缺点。

J S P的真正对手是A S P和P H P,还有即将问世的A S P +,在We b技术方面A S P、P H P和J S P的比

较见表1 - 1。

注意:这里的A S P指A S P 3 . 0,J S P指J S P规范书1 . 1中指出的规范,P H P指P H P 4。

表1-1   ASP、J S P、P H P的比较

A S P J S P P H P

We b服务器 I I S、P W S 等A p a c h e 、I I S 、P W S 、

Netscape Server等等

运行平台 Wi n d o w s 等各种U N I X(S o l a r i s、L i n u x、

A I X、I R I X等) 、Wi n d o w s

组件技术 C O M 等C O M、J a v a B e a n s

自定义TA G语法 无 等无

开放性 无 等自由软件

脚本语言支持 V B S c r i p t、J S c r i p t 等P H P

建立大型We b应用程序 可以 等不宜

程序执行速度 快 等极快

学习难度 低 等低

S e s s i o n管理 有 等有

统一的数据库连接 有、A D O、O D B C 等无

后缀名 a s p 等p h p、p h p 3、p h p s

1. We b服务器和运行平台

A S P目前仅仅被支持于Microsoft Internet Information Server(I I S)和Personal Web Server

(P W S) ,由于I I S和P W S仅仅有Wi n d o w s下的版本,故A S P目前只能在Wi n d o w s平台下使用。尽

管有第三方的插件号称可以在 U N I X下使用A S P,但对基于C O M组件技术的A S P来说,在没有

C O M支持的U N I X平台下只能是一个“玩具” 。

J S P仅仅是一个规范,尽管通过前面的论述可以得出 J S P一般要用J a v a来实现的论断,但作为

跨平台的语言,J a v a可以在许多平台下使用。这样, J S P也就显而易见的是跨平台的了。目前的

第一章 概 述计计11 下载

等A p a c h e、I I S、P W S、

Netscape Server、i P l a n e t

等各种U N I X(S o l a r i s、

L i n u x、A I X、I R I X等) 、

Wi n d o w s、M a c O S等

等J a v a B e a n s、E J B

等有

等多家合作,包括 S U N、

I B M、 BEA  We b l o g i c、

N e t s c a p e、O r a c l e

等J a v a、E M A C - S c r i p t、

We b L等

等可以

等极快

等较低

等有

等有、J D B C

等j s pJ S P的确可以在多种We b服务器和操作系统下使用。如Apache Web Server和Microsoft IIS等。

Apache Web Server是世界上占有率最高的We b服务器产品,可以在包括SUN Solaris、I B M

A I X、SGI IRIX、L i n u x和Wi n d o w s在内的许多操作系统下运行。 Apache Web Server下J S P的实

现可以通过免费的 Apache Jserv和G N U J S P、J a k a r t a - To m c a t实现,也可以使用商业的 J R U N

(L i v e S o f t w a r e) 、We b l o g i c(B E A) 、We b s p h e r e(I B M)来实现。

Microsoft IIS本身不直接支持J S P,但可以通过J R U N、We b l o g i c、We b s p h e r e来实现。

还可以使用应用服务器添加J S P支持的Netscape Enterprise Server及由之发展而来的可以直接

支持J S P的iPlanet Web Server等等。

P H P本身就对各种操作系统和We b服务器做了支持,P H P目前可以作为A p a c h e的一个附加模

块直接编译进入A p a c h e中去,由于A p a c h e支持多种操作系统,P H P相应地也就可以在各种操作

系统上实现。P H P也可以C G I方式或I S A P I方式插入到I I S或P W S中去。

2. 组件技术

A S P和J S P对组件技术的支持已经很完善了, 而P H P直到前不久才开始支持C O M和J a v a B e a n s。

但支持也不是很完善,如果P H P不能在将来完善对组件技术的支持,在大型 We b应用程序方面将

很难与J S P和A S P竞争。但由于P H P技术本身的易学易用,加上众多的函数支持和开放源代码的

特性,在中小型We b站点的开发上,P H P还是会占有一席之地的。

其实,J S P本身对于A S P和P H P并没有明显的优势,J S P的强大是因为其后面有强大的J a v a技

术做支持。包括J a v a B e a n s和J 2 E E技术在内的J a v a技术是J S P强大生命力的所在。

M i c r o s o f t最新推出的A S P +技术和A S P技术相比有了许多激动人心的进步,但是从企业级应

用的角度看,J S P技术仍然有相当的优势。有理由认为,在将来的 We b开发中,中小型站点将出

现J S P、A S P +和P H P三分天下的局面,但是对于大型的电子商务站点, J S P及J 2 E E技术将成为首

选。

1.3    用J S P开发We b的几种主要方式

J S P作为J 2 E E的一部分,既可以用于开发小型的 We b站点、也可以用于开发大型的、企业级

的应用程序,本节将讲述对于不同规模的We b系统,使用J S P进行开发的不同方式。

1.3.1   直接使用J S P

对于最小型的We b站点,可以直接使用J S P来构建动态网页,这种站点最为简单,所需要的

仅仅是简单的留言板、动态日期等基本的功能。对于这种开发模式,一般可以将所有的动态处

理部分都放置在J S P的S c r i p t l e t中,就像一般使用P H P或A S P开发动态网页一样。

1.3.2   JSP+JavaBeans

中型站点面对的是数据库查询、用户管理和小量的商业业务逻辑。对于这种站点,不能将

所有的东西全部交给J S P页面来处理。在单纯的J S P中加入J a v a B e a n s技术将有助于这种中型网站

的开发。利用J a v a B e a n s,将很容易完成如数据库连接、用户登录与注销、商业业务逻辑封装的

任务。如:将常用的数据库连接写为一个 Java Beans,既方便了使用,又可以使 J S P文件简单而

12计计第一部分 JSP    入 门

下载清晰,通过封装,还可以防止一般的开发人员直接获得数据库的控制权。

本书的第一部分将主要讲述这种开发方式,在第 2章,将简要讲述如何开发J a v a B e a n s,没有

J a v a B e a n s基础的读者不用担心。

1.3.3   JSP+JavaBeans+Servlet

无论用A S P还是P H P开发动态网站,长期以来都有一个比较重要的问题,就是网站的逻辑关

系和网站的显示页面不容易分开。常常可以看见一些夹杂着 i f . . . . . . t h e n . . . . . .、case select或是i f

{ . . . . . . }和大量显示用的H T M L代码的A S P、P H P页面,即使是有着良好的程序写作习惯的程序员,

其作品也几乎无法阅读。另一方面,动态We b的开发人员也在抱怨,将网站美工设计的静态页面

和动态程序和并的过程是一个异常痛苦的过程。

如何解决这个问题呢?在 J S P问世以后,笔者的一位朋友认为 S e r v l e t已经完全可以被J S P代

替,然而,事实是S e r v l e t在不再担负动态页面生成的任务以后,开始担负起决定整个网站逻辑

流程的任务。在逻辑关系异常复杂的网站中,借助于 S e r v l e t和J S P良好的交互关系和J a v a B e a n s的

协助,完全可以将网站的整个逻辑结构放在 S e r v l e t中,而将动态页面的输出放在 J S P页面中来完

成。在这种开发方式中,一个网站可以有一个或几个核心的 S e r v l e t来处理网站的逻辑,通过调

用J S P页面来完成客户端(通常是 We b浏览器)的请求。后面我们将可以看到,在 J 2 E E模型中,

S e r v l e t的这项功能可以被E J B取代。

1.3.4   J2EE开发模型

在J 2 E E开发模型中,整个系统可以分为三个主要的部分:

1. 视图

视图就是用户界面部分,在We b应用程序中也就是H T M L、X M L、J S P页面。这个部分主要

处理用户看到的东西,动态的 J S P部分处理了用户可以看见的动态网页,而静态的网页则由

H T M L、X M L输出。

图1 - 1

第一章 概 述计计13 下载2. 控制器。

控制器负责网站的整个逻辑。它用于管理用户与视图发生的交互。可以将控制器想像成处

在视图和数据之间,对视图如何与模型交互进行管理。通过使视图完全独立于控制器和模型,

就可以轻松替换前端客户程序,就是说,网页制作人员将可以独立自由地改变 We b页面而不用担

心影响这个基于We b的应用程序的功能。

在J 2 E E中,控制器的功能一般是由 S e r v l e t、J a v a B e a n s、Enterprise JavaBeans中的

S e s s i o n B e a n来担当的。

3. 模型

模型就是应用业务逻辑部分,这一部分的主要角色是 Enterprise JavaBeans,借助于E J B强大

的组件技术和企业级的管理控制,开发人员可以轻松形创建出可重用的业务逻辑模块。

图1 - 1说明了上述三者在J 2 E E开发模型中的相互关系。

1.4   本书用到的软件及获取方法

1. JDK

Java Development Kit即J a v a开发工具包,进行J S P开发工作必须要有J D K。本书使用版本1 . 3,

可以在h t t p : / / j a v a . s u n . c o m找到。

同样,在S U N的主页上还可以找到一份J D K的中文文档,读者可能会觉得使用本地化的文档

更方便。

2. Jakarta-To m c a t

Apache Jakarta项目组开发的基于G P L自由软件协议的J S P引擎,配合J D K就可以搭建起一个

最简单的J S P试验平台。开放源代码,使用起来比较简单,缺点是不支持 J 2 E E技术,无法使用

E J B。下载地址:h t t p : / / j a k a r t a . A p a c h e . o rg。需要说明的是,原先的G N U J S P项目的开发人员现在

已经完全转向了J a k a r t a - To m c a t项目,不建议使用A p a c h e J S e r v + G N U J S P搭建J S P平台,如果一定

要使用,也可以在h t t p : / / w w w. A p a c h e . o rg找到需要的软件。

3. Apache Web Server

仅仅使用 To m c a t来搭建 J S P的实用平台是不够的,一般小型站点使用 J S P都是采用

A p a c h e + To m c a t的方式,因为作为独立的 Web Server,To m c a t的效率显然还不够,也不具有

A p a c h e的众多实用功能。Apache Web Server也可以在h t t p : / / w w w. A p a c h e . o rg下载。

4. J2SDKEE

在S U N的主页上可以找到被称为 J 2 S D K E E的JavaTM 2 SDK,Enterprise Edition开发工具。

使用J 2 S D K E E可以用最小的开销获得一个J 2 E E的开发平台。下载地址:h t t p : / / j a v a . s u n . c o m / j 2 e e。

5. IBM We b s p h e r e

要想开发基于J 2 E E技术的We b应用程序,最好是使用一个商业化的应用服务器( A p p l i c a t i o n

S e r v e r) ,I B M的We b s p h e r e作为应用服务器在国内使用得比较多,文档和例子也不少,学习起来

比较方便。I B M公司提供免费的试用版供下载,可以在 I B M公司的主页上找到。I B M公司在国内

也发布了不少光盘版的试用版,在各大F T P站点上也有下载。

6. BEA We b l o g i c

14计计第一部分 JSP    入 门

下载作为业界领先的应用服务器提供商, BEA We b l o g i c应用服务器在世界上的市场占有率遥遥

领先于其他的对手,不过 BEA We b l o g i c应用服务器的使用难度大于 IBM We b s p h e r e,而且文档

几乎都是英文的,国内读者可能会觉得比较困难。 BEA We b l o g i c应用服务器在B E A S Y S的主页

上提供试用版本的下载地址为。h t t p : / / w w w. b e a s y s . c o m或h t t p : / / w w w. We b l o g i c . c o m。

7. UltraEdit

U l t r a E d i t并不是一个J a v a工具,但笔者认为它是一个比较好的 J S P写作工具,在U l t r a E d i t的

主页上可以得到U l t r a E d i t的试用版和支持J a v a 2和J S P的语法文件地址为。 h t t p : / / w w w. u l t r a e d i t

. c o m。

读者亦可以从其他的 J S P引擎入手学习J S P,如LiveSoftware JRUN、Inprise Application

S e r v e r、S e r v l e t E x e c等等,也有许多好的 J a v a开发工具和文本编辑器可以使用,如: I n p r i s e

Jbuilder 3.5、IBM Virsual Age For Java、Symantec Virsual Café、E d i t P l u s、J a v a P a d等等。

最后说明一下本书的代码写作规范:

类名:类名由几个单词构成时,将这几个单词紧靠在一起,且大写每一个单词的首字母。

其他:和类名相仿,但是第一个单词的首字母小写。

每个程序块统一缩进一个Ta b制表符。但是对于那种长代码较多的代码段,统一取消缩进。

注释单独列为一行。

每行代码长度不超过6 0个字符(为了出版的需要) 。

如:

class TestClass{

int a;

void TestFuncs() {

......

}

}

第一章 概 述计计15 下载

下载

第2章 预备知识

2.1   Java程序设计基础

J a v a是J S P的基础,要学习J S P技术,J a v a基础是必不可少的。本节将简要介绍 J a v a的基本语

法和概念。已经是J a v a编程人员的读者就不用阅读了,这里是为没有多少 J a v a经验的读者提供一

个快速入门的方法。这里对J a v a语言的介绍仅仅是一个基本的概况,要想深入学习 J S P,必须对

J a v a语言有深刻的理解,笔者推荐机械工业出版社翻译出版的《 J a v a编程思想》一书,本书限于

篇幅,就不多讲了。

2.1.1   Java语言规则

J a v a语言的基本结构像C / C + +, 任何用面向过程语言编写过程序的人都可以了解 J a v a语言的

大部分结构。

1. 程序结构

J a v a语言的源程序代码由一个或多个编译单元 ( c o m p i l a t i o n u n i t )组成,每个编译单元只能包

含下列内容(空格和注释除外):

• 程序包语句(package statement )。

• 入口语句(import statements) 。

• 类的声明(class declarations) 。

• 界面声明(interface declarations)。

每个J a v a的编译单元可包含多个类或界面,但是每个编译单元最多只能有一个类或者界面是

公共的。Java 的源程序代码被编译后,便产生了 J a v a字节代码。J a v a的字节代码由 一系列不依

赖于机器的指令组成,这些指令能被J a v a的运行系统(runtime system)有效地解释。J a v a的运行系

统工作起来如同一台虚拟机。在当前的J a v a实现中,每个编译单元就是一个以. j a v a为后缀的文件。

每个编译单元有若干个类,编译后,每个类生成一个 . c l a s s文件。. c l a s s文件是J a v a虚拟机能够识

别的代码。 在引入了J A R这个概念以后,现在可以把许多J a v a的c l a s s文件压缩进入一个J A R文件

中。新版本的J a v a已经可以直接读取J A R文件加以执行。

2. 注释

注释有三种类型: 

/ / 注释一行

/ * 一行或多行注释 */ 

/ * * 文档注释 **/ 

文档注释一般放在一个变量或函数定义之前,表示在任何自动生成文档系统中调入,提取

注释生成文档的工具叫做 j a v a d o c,其中还包括一些以 @开头的变量,如: @ s e e、@ v e r s i o n、@ p a r a m等等,具体用法参见J D K自带的工具文档。

3. 标识符

变量、函数、类和对象的名称都是标识符,程序员需要标识和使用的东西都需要标识符。

在J a v a语言里,标识符以字符_或$开头,后面可以包含数字,标识符是大小写有区别的,没有长

度限制。

有效的标识符如: gogogo brood_war Hello _and_you  $bill。

声明如:

int a_number; 

char _onechar; 

float $bill。

以下为J a v a的关键字:

a b s t r a c t c o n t i n u e f o r new     switch 

b o o l e a n d e f a u l t g o t o n u l l synchronized 

b r e a k d o i f p a c k a g e this 

b y t e d o u b l e i m p l e m e n t s p r i v a t e threadsafe 

b y v a l u e e l s e i m p o r t p r o t e c t e d throw 

c a s e e x t e n d s i n s t a n c e o f p u b l i c transient 

c a t c h f a l s e i n t r e t u r n true 

char  f i n a l i n t e r f a c e short  try 

c l a s s f i n a l l y l o n g s t a t i c void 

c o n s t f l o a t n a t i v e s u p e r while 

以下单词被保留使用:c a s t、f u t u r e、g e n e r i c、i n n e r、o p e r a t o r、o u t e r、r e s t、v a r。

4. 数据类型

J a v a使用五种基本类型: i n t e g e r (整数),f l o a t i n g (浮点数),p o i n t (指针),B o o l e a n (布尔变量),

Character or String(字符或字符串)。此外,还有一些复合的数据类型,如数组等。

Integer   包含下面几种类型:

整数长度 ( B i t s ) 数据类型表示

8 b y t e

1 6 short 

3 2 int 

6 4 l o n g

floating   下边给出的数据表示都是浮点数的例子:

3 . 1 4 1 5 9,3 . 1 2 3 E 1 5,4 e 5

浮点数长度(Bits)  数据类型表示

3 2 f l o a t

6 4 double 

Boolean    下边是布尔变量的两种可能取值:

t r u e false 

Character   下边给出的都是字符的例子:

第二章 预 备 知 识计计17 下载a s d f

String   下边给出的都是字符串的例子:

"gogogo,rock and roll" " J S P高级编程"

数组 可以定义任意类型的数组,如char s[],这是字符型数组; int array [],这是整型数组;

还可以定义数组的数组. intblock[][]=new int [2][3];数组边界在运行时被检测,以避免堆栈溢出。

在J a v a里,数组实际上是一个对象,数组有一个成员变量: l e n g t h。可以用这个成员函数来

查看任意数组的长度。

在J a v a里创建数组,可使用两种基本方法:

1) 创建一个空数组。

int list[]=new int[50]; 

2) 用初始数值填充数组. 

String names[] = { "Chenji","Yuan","Chun","Yang" };

它相当于下面功能: 

String names[]; 

names = new String[4]; 

names[0]=new String("Chenji"); 

names[1]=new String("Yuan"); 

names[2]=new String("Chun");

names[3]=new String("Yang"); 

在编译时不能这样创建静态数组:

int name[50]; / /将产生一个编译错误

也不能用n e w操作去填充一个没定义大小的数组。如:

int name[]; 

for (int i=0;i<9; i++) {

name[i] = i;

}

5. 表达式

J a v a语言的发展中有许多是从C语言借鉴而来的,所以J a v a的表达式和C语言非常类似。

运算符

运算符( o p e r a t o r )优先级从高到低排列如下: . 

[ ] () ++ -- ! ~ instanceof * / % + - << >> >>> < > <= >/ == ! = & ^ && || ? : = op =  ,

(2) 整数运算符

在整数运算时,如果操作数是l o n g类型,则运算结果是l o n g类型,否则为i n t类型,绝不会是

b y t e,s h o r t或c h a r型。这样,如果变量i被声明为s h o r t或b y t e,i + 1的结果会是i n t。如果结果超过

该类型的取值范围,则按该类型的最大值取模。单目整数运算符是:

运算符 操作

- 非

~ 位补码

+ + 加1

- - 减1

18计计第一部分 JSP    入 门

下载+ +运算符用于表示直接加 1操作。增量操作也可以用加运算符和赋值操作间接完成。 + +

lvalue (左值表示l v a l u e + = 1, ++lvalue 也表示lvalue =lvalue +1 (只要l v a l u e没有副作用)。- -运

算符用于表示减1操作。+ +和- -运算符既可以作为前缀运算符,也可以作为后缀运算符。 双目

整数运算符是:

运算符 操作

+ 加

- 减

* 乘

/ 除

% 取模

& 位与

| 位或

^ 位异或

< < 左移

> > 右移(带符号)

> > > 添零右移

整数除法按零舍入。除法和取模遵守以下等式: ( a/b ) * b + ( a%b ) == a。整数算术运算的

异常是由于除零或按零取模造成的。它将引发一个算术异常,下溢产生零,上溢导致越界。例

如:加1超过整数最大值,取模后,变成最小值。一个 o p =赋值运算符,和上表中的各双目整数

运算符联用,构成一个表达式。整数关系运算符 <,>,< =,> =,= =和! =产生b o o l e a n类型的数

据。

( 3 ) 布尔运算符

布尔( b o o l e a n )变量或表达式的组合运算可以产生新的 b o o l e a n值。 单目运算符!是布尔非。

双目运算符&、|和^是逻辑A N D、O R和X O R运算符,它们强制两个操作数求布尔值。为避免

右侧操作数冗余求值,用户可以使用短路求值运算符&& 和||。用户可以使用= =和! =,赋值

运算符也可以用 & =、| =、^ =。 三元条件操作符? : 和C语 言中的一样。

(4) 浮点运算符

浮点运算符可以使用常规运算符的组合,如单目运算符 + +、- -,双目运算符+、-、* 和/,

以及赋值运算符+ =,- =,* =,和/ =。 此外,还有取模运算:%和% =也可以用于浮点数,例如:

a % b和a-((int) (a/b)*b)的语义相同。这表示a % b的结果是除完后剩下的浮点数部分。只有单精度

操作数的浮点表达式按照单精度运算求值,产生单精度结果。如果浮点表达式中含有一个或一

个以上的双精度操作数,则按双精度运算,结果是双精度浮点数。

(5) 数组运算符

数组运算符形式如下:

<expression> [ <expression>] 

可给出数组中某个元素的值。合法的取值范围是从 0到数组的长度减1。取值范围的检查只

在运行时刻实施。

( 6 ) 对象运算符

双目运算符instanceof 测试某个对象是否是指定类或其子类的实例。例如:

if (myObject instanceof MyClass) { 

第二章 预 备 知 识计计19 下载MyClass anothermyObject=( MyClass) myObject; 

...

}

是判定m y O b j e c t是否是M y C l a s s的实例或是其子类的实例。

(7) 强制和转换

J a v a语言和解释器限制使用强制和转换,以防止出错导致系统崩溃。整数和浮点数间可以来

回强制转换,但整数不能强制转换成数组或对象。对象不能被强制为基本类型。

6. Java流控制

下面几个控制结构是从C语言借鉴的。

( 1 ) 分支结构

i f / e l s e分支结构:

if (Boolean) {

statemanets; 

}

else {

statements;

s w i t c h分支结构:

switch(expr1) { 

case expr2: 

statements; 

break;  

case expr3:  

statements; 

break; 

default: 

statements;  

break; 

}

(2) 循环结构

f o r循环结构:

for (init expr1;test expr2;increment expr3) {

statements;

W h i l e循环结构:

While(Boolean) { 

statements;

D o循环结构:

do 

statements; 

} while (Boolean); 

20计计第一部分 JSP    入 门

下载2.1.2   Java变量和函数

J a v a的类包含变量和函数。数据变量可以是原始的类型,如 i n t、c h a r等。成员函数是可执行

的过程。例如,下面的程序:

public class TestClass

public TestClass () { 

i=10;   

public void addI(int j) {

i=i+j;

}

Te s t C l a s s包含一个变量i和两个成员函数,TestClass(int first)和addI(int j)。

成员函数是一个可被其他类或自己类调用的处理子程序。一个特殊的成员函数叫构造函数,

这个函数名称一般与本类名称相同。它没有返回值。

在J a v a里定义一个类时,可定义一个或多个可选的构造函数,当创建本类的一个对象时,用

某一个构造函数来初始化本对象。用前面程序例子来说明,当 Te s t C l a s s类创建一个新实例时,

所有成员函数和变量被创建(创建实例)。构造函数被调用。

TestClass testObject;

testObject = new TestClass(); 

关键词n e w用来创建一个类的实例,一个类用 n e w初始化前并不占用内存,它只是一个类型

定义,当t e s t O b j e c t对象初始化后,t e s t O b j e c t对象里的i变量等于1 0。可以通过对象名来引用变量

i。(有时称为实例变量) testObject.i++;// testObject实例变量加1,因为t e s t O b j e c t有Te s t C l a s s类的

所有变量和成员函数,可以使用同样的语法来调用成员函数 a d d I:addI(10); 现在t e s t O b j e c t . i变

量等于2 1。

J a v a并不支持析构函数( C + +里的定义),因为j a v a对象无用时,有自动清除的功能,同时 它

也提供了一个自动垃圾箱的成员函数,在清除对象时被调用:

Protected void finalize() { close(); } 

2.1.3   子类

子类是利用存在的对象创建一个新对象的机制,比如,如果有一个 H o r s e类,你可以创 建一

个 Z e b r a子类,Z e b r a是H o r s e的一种。

class Zebra extends Horse { int number_OF_stripes: } 

关键词e x t e n d s来定义对象有的子类. Z e b r a是H o r s e的子类。H o r s e类里的所有特征都将拷贝到

Z e b r a类里,而Z e b r a类里可以定义自己的成员函数和实例变量。Z e b r a称为H o r s e的派生类或继承。

另外,你也许还想覆盖基类的成员函数,可用 Te s t C l a s s说明,下面是一派生类 覆盖A d d I功能的

例子。

import TestClass; 

public class NewClass extends TestClass {

第二章 预 备 知 识计计21 下载public void AddI(int j) { 

i=i+(j/2); 

}

当N e w C l a s s类的实例创建时,变量i初始化值为1 0,但调用A d d I产生不同的结果。

NewClass newObject; 

newObject=new NewClass();

newObject.AddI(10); 

当创建一个新类时,可以标明变量和成员函数的访问层次。

public public void AnyOneCanAccess(){} public实例变量和成员函数可以由任意其他类调

用。

protected protected void OnlySubClasses(){} protected实例变量和成员函数只能被其子类调

用。

private private String CreditCardNumber; private实例变量和成员函数只能在本类里调 用。

friendly void MyPackageMethod(){}是缺省的,如果没有定义任何访问控制,实例变量或函

数缺省定义成f r i e n d l y,这意味着可以被本包里的任意对象访问,但其他包里的对象不可访问。

对于静态成员函数和变量,有时候,你创建一个类,希望这个类的所有实例都公用一个变

量。就是说,所有这个类的对象都只有实例变量的同一个拷贝。这种方法的关键词为 s t a t i c, 例

如:

class Block { 

static int number=50; 

所有从B l o c k类创建的对象的n u m b e r变量值都是相同的。无任在哪个对象里改变了 n u m b e r的

值, 所有对象的n u m b e r都跟着改变。同样,可以定义s t a t i c成员函数,但这个成员函数不能访问

非s t a t i c函数和变量。

class Block {

static int number = 50; 

int localvalue; 

static void add_local(){ 

localvalue++;  //没有运行

static void add_static() {

number++;//运行

2.1.4   this和s u p e r

访问一个类的实例变量时,t h i s关键词是指向这个类本身的指针,在前面的Te s t C l a s s例子中,

可以增加构造函数如下:

public class TestClass { 

22计计第一部分 JSP    入 门

下载int i;

public TestClass() {

i = 10; 

public TestClass (int value) {

this.i = value;

public void AddI(int j) {

i = i + j; 

这里,t h i s指向Te s t C l a s s类的指针。如果在一个子类里覆盖了父类的某个成员函数,但又想

调用父类的成员函数,可以用super 关键词指向父类的成员函数。

import TestClass; 

public class NewClass extends TestClass {

public void addI (int j) {

i = i+(j/2); 

super.addI (j);

}

下面程序里,i变量被构造函数设为1 0,然后为1 5,最后被父类( Te s t C l a s s )设为2 5。

NewClass newObject; 

newObject = new NewClass();

newObject.addI(10); 

2.1.5   类的类型

迄今为止,在类前面只用了一个p u b l i c关键词,其实它有下面4种选择:

a b s t r a c t。一个a b s t r a c t类必须至少有一个虚拟函数,一个 a b s t r a c t类不能直接创建对象,必须

继承子类后才能创建对象。

f i n a l一个f i n a l类声明了子类链的结尾,用f i n a l声明的类不能再派生子类。

P u b l i c。p u b l i c类能被其他的类访问。在其他包里,如果想使用这个类,必须先 i m p o r t,否

则它只能在它定义的p a c k a g e里使用。

s y n c h r o n i c a b l e。这个类标识表示所有类的成员函数都是同步的。

2.1.6   抽象类

面向对象的一个最大优点就是能够定义怎样使用这个类而不必真正定义好成员函数。当程

序由不同的用户实现时,这是很有用的,这不需用户使用相同的成员函数名。

在j a v a里,G r a p h i c s类中一个a b s t r a c t类的例子如下:

public abstract class Graphics {

public abstract void drawLine(int x1,int y1,int x2, int y2);

public abstract void drawOval(int x,int y,int width, int height); 

第二章 预 备 知 识计计23 下载public abstract void drawRect(int x,int y,int width, int height); 

在G r a p h i c s类里声明了几个成员函数,但成员函数的实际代码是在另外一个地方实现的。

public class MyClass extends Graphics { public void drawLine (int x1,int y1,int x2,

int y2) { <画线程序代码> } } 

当一个类包含一个a b s t r a c t成员函数时,这个类必须定义为a b s t r a c t类。然而并不是a b s t r a c t类

的所有成员函数都是a b s t r a c t的。A b s t r a c t类不能有私有成员函数(它们不能被实现),也不能有静

态成员函数。

2.1.7   接口

当确定多个类的操作方式都很相像时, a b s t r a c t成员函数是很有用的。但如果需要使用这个

a b s t r a c t成员函数,必须创建一个新类,这样有时很繁琐。接口提供了一种抽象成员函数的有利

方法。一个接口包含了在另一个地方实现的成员函数的收集。成员函数在接 口里定义为p u b l i c和

a b s t r a c t。 接口里的实例变量是p u b l i c、s t a t i c和f i n a l。接口和抽象的主要区别是,一个接口提供

了封装成员函数协议的方法而不必强迫用户继承类。

例如:

public interface AudiClip { 

//Start playing the clip. 

void play();    

//Play the clip in a loop.   

void loop();

//Stop playing the clip 

void stop(); 

想使用Audio Clip接口的类使用i m p l e n e n t s关键词来提供成员函数的程序代码。

class MyClass implements AudioClip {

void play(){ <实现代码> }   

void loop <实现代码> } 

void stop <实现代码> }

接口的优点是:一个接口类可以被任意多的类实现,每个类可以共享程序接口而不必关心

其他类是怎样实现的。

2.1.8   包

包( P a c k a g e )由一组类( c l a s s )和界面( i n t e r f a c e )组成。它是管理大型名字空间,避免名字冲 突

的工具。每一个类和界面的名字都包含在某个包中。按照一般的习惯,它的名字由“ .” 号分隔

的单词构成,第一个单词通常是开发这个包的组织的名称。

定义一个编译单元的包由 p a c k a g e语句定义。如果使用p a c k a g e语句,编 译单元的第一行必

须无空格,也无注释。其格式如下:

24计计第一部分 JSP    入 门

下载package packageName; 

若编译单元无p a c k a g e语句,则该单元被置于一个缺省的无名的包中。

在J a v a语言里,提供了一个包可以使用另一个包中类和界面的定义和实现的机制。用 i m p o r t

关键词来标明来自其他包中的类。一个编译单元可以自动把指定的类和界面输入到它自己 的包

中。在一个包中的代码可以有两种方式定义自其他包中的类和界面:在每个引用的类和界面前

面给出它们所在的包的名字:

//前缀包名法 acme. project.FooBar 

obj=new acme. project. FooBar( ); 

使用i m p o r t语句引入一个类或一个界面,或包含它们的包。引入的类和界面的名字在当前的

名字空间可用。引入一个包时,则该包所有的公有类和界面均可用。其形式如下:

// 从 acme.project 引入所有类

import acme.project.*; 

这个语句表示a c m e . p r o j e c t中所有的公有类被引入当前包。以下语句从 acme. project包 中进

入一个类E m p l o y e c _ L i s t。

//从 acme. project而引入Employee_List 

import acme.project.Employee_list; 

Employee_List obj = new Employee_List( ); 

在使用一个外部类或界面时,必须要声明该类或界面所在的包,否则会产生编译错误。

i m p o r t (引入)类包(class package)用i m p o r t关键词调入指定p a c k a g e名字如路径和类名,用*匹

配符可以调入多于一个类名。

import java.Date; import java.awt.*; 

如果j a v a源文件不包含p a c k a g e,它放在缺省的无名p a c k a g e。这与源文件同目 ,类可以这样

引入: 

import MyClass;

J a v a系统包:J a v a语言提供了一个包含窗口工具箱、实用程序、一般 I / O、工具和网络功能

的包。

各个包的用法和类详解在J D K自带的文档中都有详细的说明,希望读者能够好好的看一看,

对大部分的常用包的类都熟悉了,才能更好地掌握 J a v a这门技术。

在了解J a v a语言的概况以后,接下来看一看与J S P技术密切相关的三种J a v a技术:J a v a E e a n s,

J D B C,Java Servlet。

2.2   JavaBeans

J a v a B e a n s是什么?J a v a B e a s n s是一个特殊的类,这个类必须符合J a v a B e a n s规范。J a v a B e a n s

原来是为了能够在一个可视化的集成开发环境中可视化、模块化地利用组件技术开发应用程序

而设计的。不过,在J S P中,不需要使用任何可视化的方面,但仍然需要利用 J a v a B e a n s的属性、

事件、持久化和用户化来实现模块化的功能。下面分别介绍 J a v a B e a n s的属性、事件、持久化和

用户化。

第二章 预 备 知 识计计25 下载2.2.1   JavaBeans的属性

J a v a B e a n s的属性与一般J a v a程序中所指的属性,或者说与所有面向对象的程序设计语言中

对象的属性是一个概念,在程序中的具体体现就是类中的变量。在 J a v a B e a n s设计中,按照属性

的不同作用又细分为四类:Simple, Index, Bound与C o n s t r a i n e d属性。

1. Simple属性

Simple 属性表示伴随有一对g e t / s e t方法(C语言的过程或函数在J a v a程序中称为”方法” )的

变量。属性名与和该属性相关的g e t / s e t方法名对应。例如:如果有s e t X和g e t X方法,则暗指有一

个名为”X”的属性。如果有一个方法名为i s X,则通常暗指”X”是一个布尔属性(即X的值为

t r u e或f a l s e) 。例如,在下面这个程序中:

public class alden1 extends Canvas {

string ourString= "Hello"; 

//属性名为ourString,类型为字符串

public alden1(){ 

//alden1()是alden1的构造函数,与C++中构造函数的意义相同

setBackground(Color.red);

setForeground(Color.blue);

}

/* "set"属性*/

public void setString(String newString) {

ourString=newString;

}

/* "get"属性 */

public String getString() {

return ourString;

}

}

2. Indexed属性

I n d e x e d属性表示一个数组值。使用与该属性对应的 s e t / g e t方法可取得数组中的数值。该属

性也可一次设置或取得整个数组的值。例如:

public class alden2 extends Canvas {

int[] dataSet={1,2,3,4,5,6};

// dataSet是一个indexed属性

public alden2() {

setBackground(Color.red);

setForeground(Color.blue);   

}

/* 设置整个数组 */

public void setDataSet(int[] x){

dataSet=x;

}

/* 设置数组中的单个元素值 */

public void setDataSet(int index, int x){

dataSet[index]=x;

26计计第一部分 JSP    入 门

下载}

/* 取得整个数组值 */

public int[] getDataSet(){

return dataSet;

}

/* 取得数组中的指定元素值 */

public int getDataSet(int x){

return dataSet[x];

}

}

3. Bound属性

B o u n d属性是指当该种属性的值发生变化时,要通知其他的对象。每次属性值改变时,这种

属性就触发一个P r o p e r t y C h a n g e事件(在J a v a程序中,事件也是一个对象)。事件中封装了属性名、

属性的原值、属性变化后的新值。这种事件传递到其他的 B e a n s,至于接收事件的B e a n s应做什

么动作,由其自己定义。

当P u s h B u t t o n的b a c k g r o u n d属性 与D i a l o g的b a c k g r o u n d属性绑定时,若 P u s h B u t t o n的

b a c k g r o u n d属性发生变化,D i a l o g的b a c k g r o u n d属性也发生同样的变化。例如:

public class alden3 extends Canvas{

String ourString= "Hello"; 

//ourString是一个bound属性

private PropertyChangeSupport changes = new PropertyChangeSupport(this);

/*Java是纯面向对象的语言,如果要使用某种方法则必须指明是要使用哪个对象的方法,在下面的程序中要进行点

火事件的操作,这种操作所使用的方法是在PropertyChangeSupport类中的。所以上面声明并实例化了一个

changes对象,在下面将使用changes的firePropertyChange方法来点火ourString的属性改变事件。*/ 

public void setString(string newString){

String oldString = ourString;

ourString = newString;

/* ourString的属性值已发生变化,于是接着点火属性改变事件 */

changes.firePropertyChange("ourString",oldString,newString);

}

public String getString(){

return ourString;

}

/** 以下代码是为开发工具所使用的。我们不能预知alden3将与哪些其他的Beans组合成为一个应用,无法预知若

alden3的ourString属性发生变化时有哪些其他的组件与此变化有关,因而alden3这个Beans要预留出一些接口

给开发工具,开发工具使用这些接口,把其他的JavaBeans对象与alden3挂接。*/ 

public void addPropertyChangeListener(PropertyChangeLisener l){

changes.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(PropertyChangeListener l){

changes.removePropertyChangeListener(l);

}

第二章 预 备 知 识计计27 下载通过上面的代码,开发工具调用c h a n g e s的a d d P r o p e r t y C h a n g e L i s t e n e r方法把其他J a v a B e a n注

册入o u r S t r i n g属性的监听者队列l中,l是一个Ve c t o r数组,可存储任何J a v a对象。开发工具也可

使用c h a n g e s的r e m o v e P r o p e r t y C h a n g e L i s t e n e r方法,从l中注销指定的对象,使a l d e n 3的o u r S t r i n g

属性的改变不再与这个对象有关。当然,当程序员手写代码编制程序时,也可直接调用这两个

方法,把其他J a v a对象与a l d e n 3挂接。

4. Constrained属性

J a v a B e a n s的Co n s t r a i n e d属性是指当这个属性的值要发生变化时,与这个属性已建立了某种连

接的其他J a v a对象可否决属性值的改变。Co n s t r a i n e d属性的监听者通过抛出P r o p e r t y Ve t o E x c e p t i o n

来阻止该属性值的改变。

例如:下面程序中的Co n s t r a i n e d属性是P r i c e I n C e n t s。

public class JellyBean extends Canvas{

private PropertyChangeSupport changes=new PropertyChangeSupport(this);

private VetoableChangeSupport Vetos=new VetoableChangeSupport(this);

/*与前述changes相同,可使用VetoableChangeSupport对象的实例Vetos中的方法,在特定条件下来阻

止PriceInCents值的改变。*/ 

......

public void setPriceInCents(int newPriceInCents) throws PropertyVetoException {

/*方法名中throws PropertyVetoException的作用是当有其他Java对象否决PriceInCent

s的改变时,要抛出例外。*/ 

/* 先保存原来的属性值*/ 

int oldPriceInCents=ourPriceInCents;

/**点火属性改变否决事件*/ 

vetos.fireVetoableChange

("priceInCents",new Integer(OldPriceInCents), new Integer(newPriceInCents));

/**若有其他对象否决priceInCents的改变,则程序抛出例外,不再继续执行下面的两条语句,方法结束。若无其

他对象否决priceInCents的改变,则在下面的代码中把ourPriceIncents赋予新值,并点火属性改变事件*/ 

ourPriceInCents=newPriceInCents;

changes.firePropertyChange

("priceInCents", new Integer(oldPriceInCents),new  Integer(newPriceInCents));

}

/**与前述changes相同,也要为PriceInCents属性预留接口,使其他对象可注册入PriceInCents否决改变监

听者队列中,或把该对象从中注销

public void addVetoableChangeListener(VetoableChangeListener l){

vetos.addVetoableChangeListener(l);

}

public void removeVetoableChangeListener(VetoableChangeListener l){

vetos.removeVetoableChangeListener(l);

}

......

从上面的例子中可看到,一个 Co n s t r a i n e d属性有两种监听者:属性变化监听者和否决属性

改变的监听者。否决属性改变的监听者在自己的对象代码中有相应的控制语句,在监听到有

Co n s t r a i n e d属性要发生变化时,在控制语句中判断是否应否决这个属性值的改变。

28计计第一部分 JSP    入 门

下载总之,某个B e a n s的Co n s t r a i n e d属性值可否改变取决于其他的B e a n s或者是J a v a对象是否允许

这种改变。允许与否的条件由其他的B e a n s或J a v a对象在自己的类中进行定义。

2.2.2   JavaBeans的事件

事件处理是J a v a B e a n s体系结构的核心之一。通过事件处理机制,可让一些组件作为事件源,

发出可被描述环境或其他组件接收的事件。这样,不同的组件就可在构造工具内组合在一起,

组件之间通过事件的传递进行通信,构成一个应用。从概念上讲,事件是一种在“源对象”和

“监听者对象”之间某种状态发生变化的传递机制。事件有许多不同的用途,例如在 Wi n d o w s系

统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在 J a v a和J a v a B e a n s中则定义了一

个一般的、可扩充的事件机制,这种机制能够:

• 对事件类型和传递模型的定义和扩充提供一个公共框架,并适合于广泛的应用。

• 与J a v a语言和环境有较高的集成度。

• 事件能被描述环境捕获和触发。

• 能使其他构造工具采取某种技术在设计时直接控制事件、事件源和事件监听者之间的联

系。

• 事件机制本身不依赖于复杂的开发工具。

特别地,还应当:

• 能够发现指定的对象类可以生成的事件。

• 能够发现指定的对象类可以观察(监听)到的事件。

• 提供一个常规的注册机制,允许动态操纵事件源与事件监听者之间的关系。

• 不需要其他的虚拟机和语言即可实现。

• 事件源与监听者之间可进行高效的事件传递。

• 能完成J a v a B e a n事件模型与相关的其他组件体系结构事件模型的中立映射。

1. 概述

J a v a B e a n s事件模型总体结构的主要构成: 事件从事件源到监听者的传递是通过对目标监听

者对象的J a v a方法调用进行的。 对每个明确的事件发生,都相应地定义一个明确的J a v a方法。这

些方法都集中定义在事件监听者(E v e n t L i s t e n e r)接口中,这个接口要继承j a v a . u t i l . E v e n t L i s t e n e r。

实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态

通常都封装在事件状态对象中,该对象必须继承自 j a v a . u t i l . E v e n t O b j e c t。事件状态对象作为单参

传递给应响应该事件的监听者方法中。 发出某种特定事件的事件源的标识是:遵从规定的设计格

式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。 有时,事件监听者不

能直接实现事件监听者接口,或者还有其他的额外动作时,就要在一个源与其他一个或多个监听

者之间插入一个事件适配器类的实例,以建立它们之间的联系。

2. 事件状态对象

与事件发生有关的状态信息一般都封装在一个事件状态对象中,这种对象是j a v a . u t i l . E v e n t O b j e c t

的子类。按设计习惯,这种事件状态对象类名应以E v e n t结尾。例如:

public class MouseMovedExampleEvent extends java.util.EventObject{ 

第二章 预 备 知 识计计29 下载protected int x, y;

/* 创建一个鼠标移动事件MouseMovedExampleEvent */

MouseMovedExampleEvent(java.awt.Component source, Point location) { 

super(source);

x = location.x;

y = location.y;

}

/* 获取鼠标位置*/

public Point getLocation() { 

return new Point(x, y);

}

}

3. 事件监听者接口与事件监听者

由于J a v a事件模型是基于方法调用的,因而需要一个定义并组织事件操纵方法的方式。

J a v a B e a n s中,事件操纵方法都被定义在继承了j a v a . u t i l . E v e n t L i s t e n e r类的事件监听者(E v e n t L i s t e n e r)

接口中,按规定,E v e n t L i s t e n e r接口的命名要以L i s t e n e r结尾。任何一个类如果想操纵在E v e n t L i s t e n e r

接口中,定义的方法都必须以实现这个接口方式进行。这个类就是事件监听者。

例如:

/*先定义了一个鼠标移动事件对象*/

public class MouseMovedExampleEvent extends java.util.EventObject {

// 在此类中包含了与鼠标移动事件有关的状态信息

... 

}

/*定义了鼠标移动事件的监听者接口*/

interface MouseMovedExampleListener extends java.util.EventListener { 

/*在这个接口中定义了鼠标移动事件监听者所应支持的方法*/

void mouseMoved(MouseMovedExampleEvent mme);

}

在接口中只定义方法名,方法的参数和返回值类型。如上面接口中的 m o u s e M o v e d方法的具

体实现是在下面的A r b i t r a r y O b j e c t类中定义的:

class ArbitraryObject implements MouseMovedExampleListener { 

public void mouseMoved(MouseMovedExampleEvent mme) {

...

}

A r b i t r a r y O b j e c t就是M o u s e M o v e d E x a m p l e E v e n t事件的监听者。

4. 事件监听者的注册与注销

为了让各种可能的事件监听者把自己注册入合适的事件源中,就建立源与事件监听者间的

事件流,事件源必须为事件监听者提供注册和注销的方法。在前面的 b o u n d属性介绍中,已看到

了这种使用过程,在实际中,事件监听者的注册和注销要使用标准的设计格式:

public void add< ListenerType>(< ListenerType> listener);

public void remove< ListenerType>(< ListenerType> listener);

例如:

30计计第一部分 JSP    入 门

下载首先定义了一个事件监听者接口:

public interface ModelChangedListener extends java.util.EventListener { 

void modelChanged(EventObject e); 

}

接着定义事件源类:

public abstract class Model { 

private Vector listeners = new Vector(); 

// 定义了一个存储事件监听者的数组

/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/

public synchronized void addModelChangedListener(ModelChangedListener mcl){

listeners.addElement(mcl); 

}

//把监听者注册入listeners数组中

public synchronized void removeModelChangedListener(ModelChangedListener mcl){

listeners.removeElement(mcl); 

//把监听者从listeners中注销

}

/*以上两个方法的前面均冠以synchronized,是因为运行在多线程环境时,可能同时有几个对象同时要进行注册

和注销操作,使用synchronized来确保它们之间的同步。开发工具或程序员使用这两个方法建立源与监听者之间的事件

流*/ 

protected void notifyModelChanged() {

/**事件源使用本方法通知监听者发生了modelChanged事件*/

Vector l; 

EventObject e = new EventObject(this);

/* 首先要把监听者拷贝到l数组中,冻结EventListeners的状态以传递事件。这样来确保在事件传递到所有监

听者之前,已接收了事件的目标监听者的对应方法暂不生效。*/ 

synchronized(this) { 

l = (Vector)listeners.clone(); 

}

for (int i = 0; i < l.size(); i++) {

/* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件,并把事件状态对象e作为参数传递

给监听者队列中的每个监听者*/

((ModelChangedListener)l.elementAt(i)).modelChanged(e); 

}

}

在程序中可见,事件源M o d e l类显式地调用了接口中的m o d e l C h a n g e d方法,实际是把事件状

态对象e作为参数,传递给了监听者类中的m o d e l C h a n g e d方法。

5. 适配类

适配类是J a v a事件模型中极其重要的一部分。在一些应用场合,事件从源到监听者之间的传

递要通过适配类来“转发” 。例如:当事件源发出一个事件,而有几个事件监听者对象都可接收

该事件,但只有指定对象做出反应时,就要在事件源与事件监听者之间插入一个事件适配器类,

由适配器类来指定事件应该是由哪些监听者来响应。

适配类成为了事件监听者,事件源实际是把适配类作为监听者注册入监听者队列中,而真

第二章 预 备 知 识计计31 下载正的事件响应者并未在监听者队列中,事件响应者应做的动作由适配类决定。目前绝大多数的

开发工具在生成代码时,事件处理都是通过适配类来进行的。

2.2.3   持久化

当J a v a B e a n s在构造工具内被用户化,并与其他B e a n s建立连接之后,它的所有状态都应当可

被保存,下一次被装载进构造工具内或在运行时,就应当是上一次修改完的信息。为了能做到

这一点,要把B e a n s的某些字段的信息保存下来,在定义 B e a n s时要使它实现j a v a . i o . S e r i a l i z a b l e

接口。例如:

public class Button implements java.io.Serializable {

}

实现了序列化接口的B e a n s中字段的信息将被自动保存。若不想保存某些字段的信息则可在

这些字段前冠以t r a n s i e n t或s t a t i c关键字,t r a n s i e n t和s t a t i c变量的信息是不可被保存的。通常,一

个B e a n s所有公开出来的属性都应当是被保存的,也可有选择地保存内部状态。 B e a n s开发者在

修改软件时,可以添加字段,移走对其他类的引用,改变一个字段的 p r i v a t e / p r o t e c t e d / p u b l i c状

态,这些都不影响类的存储结构关系。然而,当从类中删除一个字段,改变一个变量在类体系

中的位置,把某个字段改成t r a n s i e n t / s t a t i c,或原来是t r a n s i e n t / s t a t i c,现改为别的特性时,都将

引起存储关系的变化。

J a v a B e a n s的存储格式

J a v a B e a n s组件被设计出来后,一般是以扩展名为 j a r的Z i p格式文件存储,在 j a r中包含与

J a v a B e a n s有关的信息,并以M A N I F E S T文件指定其中的哪些类是 J a v a B e a n s。以j a r文件存储的

J a v a B e a n s在网络中传送时极大地减少了数据的传输数量,并把 J a v a B e a n s运行时所需要的一些资

源捆绑在一起。

这里主要论述了J a v a B e a n s s的一些内部特性及其常规设计方法,参考的是J a v a B e a n s s规范书。

随着世界各大I S V对J a v a B e a n s s越来越多的支持,规范在一些细节上还在不断演化,但基本框架

不会再有大的变动。

2.2.4   用户化

J a v a B e a n s开发者可以给一个B e a n s添加定制器(C u s t o m i z e r) 、属性编辑器(P r o p e r t y E d i t o r)

和B e a n I n f o接口来描述一个B e a n s的内容,B e a n s的使用者可在构造环境中通过与 B e a n s附带在一

起的这些信息来用户化 B e a n s的外观和应做的动作。一个 B e a n s不必都有 B e a n C u s t o m i z e r、

P r p e r t y E d i t o r和B e a n I n f o,根据实际情况,这些是可选的,当有些 B e a n s较复杂时,就要提供这

些信息,以Wi z a r d的方式使B e a n的使用者能够定制一个B e a n s。有些简单的B e a n s可能没有这些

信息,则构造工具可使用自带的透视装置,透视出 B e a n s的内容,并把信息显示到标准的属性表

或事件表中供使用者定制 B e a n s,前几节提到的B e a n s的属性、方法和事件名要以一定的格式命

名,主要的作用就是供开发工具对 B e a n s进行透视。当然也是给程序员在手写程序中使用 B e a n s

提供方便,使其能观其名,知其意。

1. 定制器接口

32计计第一部分 JSP    入 门

下载当一个B e a n有了自己的定制器时,在构造工具内就可展现出自己的属性表。在定义定制器

时必须要实现j a v a . b e a n s . C u s t o m i z e r接口。例如,下面是一个“按钮”B e a n s的定制器:

public class OurButtonCustomizer extends Panel implements Customizer {

... ...

/*当实现像OurButtonCustomizer这样的常规属性表时,一定要在其中实现addProperChangeListener和

removePropertyChangeListener,这样,构造工具可用这些功能代码为属性事件添加监听者。*/

... ...

private PropertyChangeSupport changes=new PropertyChangeSupport(this);

public void addPropertyChangeListener(PropertyChangeListener l) {

changes.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(PropertyChangeListener l) {

changes.removePropertyChangeListener(l);

}

... ...

}

2. 属性编辑器接口

一个J a v a B e a n s可提供P r o p e r t y E d i t o r类,为指定的属性创建一个编辑器。这个类必须继承自

j a v a . b e a n s . P r o p e r t y E d i t o r S u p p o r t类。构造工具与手写代码的程序员不直接使用这个类,而是在

下一小节的B e a n I n f o中实例化并调用这个类。例如:

public class MoleculeNameEditor extends java.beans.PropertyEditorSupport {

public String[] getTags() {

String resule[]={

"HyaluronicAcid","Benzene","buckmisterfullerine",

"cyclohexane","ethane","water"};

return resule;

}

}

上例中是为Ta g s属性创建了属性编辑器,在构造工具内,从下拉表格中选择 M o l e c u l e N a m e

的属性应是”H y a l u r o n i c A i d”或“w a t e r” 。

3. BeanInfo接口

每个B e a n类也可能有与之相关的 B e a n I n f o类,在其中描述了这个 B e a n在构造工具内出现时

的外观。B e a n I n f o中可定义属性、方法、事件,显示它们的名称,提供简单的帮助说明。

例如:

public class MoleculeBeanInfo extends SimpleBeanInfo {

public PropertyDescriptor[] getPropertyDescriptors() {

try {

PropertyDescriptor pd=new PropertyDescriptor("moleculeName",Molecule.class);

/*通过pd引用了上一节的MoleculeNameEditor类,取得并返回

moleculeName属性*/

pd.setPropertyEditorClass(MoleculeNameEditor.class);

PropertyDescriptor result[]={pd};

return result;

第二章 预 备 知 识计计33 下载} catch(Exception ex) {

System.err.println("MoleculeBeanInfo: unexpected exeption: "+ex);

return null;

}

}

}

2.3   Java Servlet

鉴于J S P和Java Servlet的紧密联系,笔者认为学习 J S P一定要有Java Servlet的基础,当然,

不需要成为Java Servlet的专家,但一定要有概念上的认识。

下面将介绍Java Servlet的基础知识,对于具体的程序开发,现在不需要读者立即掌握,本

节的目的在于让读者能够对S e r v l e t有所了解。

2.3.1   HTTP Servlet API

Java Servlet 开发工具(J S D K)提供了多个软件包,在编写 Servlet 时需要用到这些软件包。

其中包括两个用于所有 Servlet 的基本软件包:javax.servlet 和 j a v a x . s e r v l e t . h t t p。可从s u n公司的

We b站点下载 Java Servlet 开发工具,现在一般使用J S D K 2 . 0。这里主要介绍j a v a x . s e r v l e t . h t t p提

供的HTTP Servlet应用编程接口。

1. 简述

创建一个 HTTP Servlet,需要扩展 HttpServlet 类,该类是用专门的方法来处理 H T M L表格

数据的 GenericServlet 的一个子类。HttpServlet 类包含 i n i t ( )、d e s t r o y ( )、service() 等方法。其中

init() 和 destroy() 方法是继承的。

(1) init() 方法

在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。可以

配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 S e r v l e t。无论有多少客户机访问

S e r v l e t,都不会重复执行 init() 。

(2)service() 方法

service() 方法是 Servlet 的核心。每当一个客户请求一个 HttpServlet 对象时,该对象的

service() 方法就要被调用,而且传递给这个方法一个“请求” (S e r v l e t R e q u e s t)对象和一个“响

应” (S e r v l e t R e s p o n s e)对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功

能是调用与 HTTP 请求的方法相应的 do 功能。例如, 如果 HTTP 请求方法为 G E T,则缺省情

况下就调用 doGet()  。Servlet  应该为 Servlet  支持的 HTTP  方法覆盖 do  功能。因为

HttpServlet.service() 方法会检查请求方法是否调用了适当的处理方法,不必覆盖 service() 方法。

只需覆盖相应的 do 方法就可以了。

(3)destroy() 方法

destroy() 方法仅执行一次,即在服务器停止且卸装 Servlet 时执行该方法。典型的,将

Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆

盖它,典型的是管理服务器端资源。例如,如果 Servlet 在运行时会累计统计数据,则可以编写

34计计第一部分 JSP    入 门

下载一个 destroy() 方法,该方法用于在未装入 Servlet 时将统计数字保存在文件中。另一个示例是关

闭数据库连接。

(4)G e t S e r v l e t C o n f i g()方法

G e t S e r v l e t C o n f i g()方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和

S e r v l e t C o n t e x t。ServletContext 接口提供有关servlet 的环境信息。

(5)G e t S e r v l e t I n f o()方法

G e t S e r v l e t I n f o()方法是一个可选的方法,它提供有关 servlet 的信息,如作者、版本、版

权。

当服务器调用sevlet 的S e r v i c e() 、d o G e t()和d o P o s t()这三个方法时,均需要“请求”和

“响应”对象作为参数。 “请求”对象提供有关请求的信息,而”响应”对象提供了一个将响应信

息返回给浏览器的通信途径。javax.servlet 软件包中的相关类为S e r v l e t R e s p o n s e和S e r v l e t R e q u e s t,

而javax.servlet.http 软件包中的相关类为HttpServletRequest 和 H t t p S e r v l e t R e s p o n s e。

Servlet 通过这些对象与服务器通信并最终与客户机通信。 Servlet 能通过调用“请求

(R e q u e s t) ”对象的方法获知客户机环境、服务器环境的信息和所有由客户机提供的信息。 S e r v l e t

可以调用“响应”对象的方法发送响应,该响应是准备发回客户机的。

2. 常用HTTP Servlet API概览

支持H T T P协议的s e r v l e t可以使用j a v a x . s e r v l e t . h t t p包进行开发,而j a v a x . s e r v l e t包中的核心功

能提供了 We b开发的许多类和函数,为进行 J S P的开发带来了极大的方便。比如,抽象

H t t p S e r v l e t类包含对不同 H T T P请求方法和头信息的支持, H t t p S e r v l e t R e q u e s t和

H t t p S e r v l e t R e s p o n s e接口允许直接与We b服务器通信,而H t t p S e s s i o n提供内置会话跟踪功能;

C o o k i e类可以很快地设置和处理HTTP Cookie,H t t p U t i l s类用于处理请求字串。

(1) Cookie

C o o k i e类提供了读取、创建和操纵HTTP Cookie的便捷途径,允许s e r v l e t在客户端存储少量

的数据。C o o k i e主要用于会话跟踪和存储少量用户配置信息数据。

S e r v l e t用H t t p S e r v l e t R e q u e s t的g e t C o o k i e()方法获取C o o k i e信息;H t t p S e r v l e t R e s p o n s e的

a d d C o o k i e()方法向客户端发送新的C o o k i e,因为是使用HTTP 头设置的,所以a d d C o o k i e()

必须在任何输出发送到客户端之前调用。

虽然Java Web Server有一个s u n . s e r v l e t . u t i l . C o o k i e类能完成大致相同的工作,但最初的

Servlet API 1.0 却没有C o o k i e类。s u n . s e r v l e t . u t i l . C o o k i e类和当前C o o k i e类唯一不同的是获取和

创建方法是C o o k i e类的静态组成部分,而不是H t t p S e r v l e t R e q u e s t和H t t p S e r v l e t R e s p o n s e的接口。

(2) HttpServlet

H t t p S e r v l e t是开发HTTP servlet框架的抽象类,,其中的s e r v i c e ( )方法将请求分配给H T T P的

protected service()方法。

(3) HttpServletRequest

H t t p S e r v l e t R e q u e s t通过扩展S e r v l e t R e q u e s t基类,为HTTP servlets提供附加的功能。它支持

C o o k i e s和s e s s i o n跟踪及获取H T T P头信息的功能;H t t p S e r v l e t R e q u e s t还能解析H T T P的表单数

据,并将其存为s e r v l e t参数。

第二章 预 备 知 识计计35 下载服务器将H t t p S e r v l e t R e q u e s t对象传给H t t p S e r v l e t的s e r v i c e ( )方法。

(4) HttpServletResponse

H t t p S e r v l e t R e s p o n s e扩展S e r v l e t R e s p o n s e类,允许操纵H T T P协议相关数据,包括响应头和

状态码。它定义了一系列常量,用于描述各种 H T T P状态码,还包含用于s e s s i o n跟踪操作的帮助

函数。

(5) HttpSession

H t t p S e s s i o n接口提供了对We b访问者的认证机制。H t t p S e s s i o n接口允许s e r v l e t查看和操纵会

话相关信息,比如创建访问时间和会话身份识别。它还包含一些方法,用于绑定会话到特定的

对象,允许“购物车”和其他的应用程序保存数据用于各连接间共享,而不必存到数据库或其

他的e x t r a - s e r v l e t资源中。

S e r v l e t调用H t t p S e r v l e t R e q u e s t的g e t S e s s i o n ( )方法来获得H t t p S e s s i o n对象,定制S E S S I O N的

行为,比如在销毁S E S S I O N之前等待的时间,依服务器而定。

虽然任何对象都可以绑定到 S E S S I O N,然而对一些事务繁忙的 Se r v l e t,绑定大的对象到

S E S S I O N中将会加重服务器的负担。减轻服务器负担最常用的解决办法是,仅仅绑定用于实现

j a v a . i o . S e r i a l i z a b l e接口的对象(它包含Java API核心中的所有数据类型对象) 。有些服务器能将

S e r i a l i z a b l e对象写入磁盘中,U n s e r i a l i z a b l e对象比如j a v a . s q l . C o n n e c t i o n ,必须保留在内存中。

(6) HttpSessionBindingEvent

H t t p S e s s i o n B i n d i n g L i s t e n e r监听对象绑定或断开绑定于会话时, H t t p S e s s i o n B i n d i n g E v e n t被

传递到H t t p S e s s i o n B i n d i n g L i s t e n e r。

(7) HttpSessionBindingListener 

当对象绑定于 H t t p S e s s i o n或从 H t t p S e s s i o n松开绑定时,通过调用 valueBound  ()和

valueUnbound ()来通知用于实现H t t p S e s s i o n B i n d i n g L i s t e n e r的接口。其他情况下,这个接口可以

顺序清除与s e s s i o n相关的资源,例如数据库连接等。

(8) HttpSessionContext

H t t p S e s s i o n C o n t e x t提供了访问服务器上所有活动 s e s s i o n的方法,这对s e r v l e t清除不活动的

s e s s i o n,显示统计信息和其他共享信息是很有用的。 S e r v l e t通过调用 H t t p S e s s i o n的

getSessionContext () 方法获得H t t p S e s s i o n C o n t e x t对象。

(9) HttpUtils

这是一个容纳许多有用的基于 H T T P方法的容器对象,使用这些方法,可以使 S e r v l e t开发更

方便。

2.3.2   系统信息

要成功建立We b应用,必须了解它所需的运行环境,还要了解执行 s e r v l e t的服务器和发送请

求的客户端的具体情况。不管应用程序运行于哪种环境,都应该确切知道应用程序所应处理的

请求信息。

S e r v l e t有许多方法可以获取这些信息。大多数情况下,每个方法都会返回不同的结果。比

较C G I用于传递信息的环境变量,读者会发现s e r v l e t方法具有以下几个优点:

36计计第一部分 JSP    入 门

下载• 强有力的类型检查。

• 延迟计算。

• 与服务器的更多交互。

1. 初始化参数

每个注册的s e r v l e t名称都有与之相关的特定参数, s e r v l e t程序在任何时候都可获得这些参

数;它们经常使用i n i t()方法来为s e r v l e t设定初始或缺省值以在一定程度上定制s e r v l e t的行为。

(1) 获得初始参数

s e r v l e t用g e t I n i t P a r a m e t e r()方法来获取初始参数:

public  String ServletConfig.getInitParameter(String name)

这个方法返回初始参数的名称或空值 (如果不存在)。返回值总是S t r i n g类型,由s e r v l e t对它

进行解释。

G e n e r i c S e v l e t类实现S e r v l e t C o n f i g接口,直接访问g e t I n i t P a r a m e t e r()方法。

(2) 获取初始参数名

s e r v l e t调用g e t I n i t P a r a m e t e r N a m e s()方法可检验它的所有参数:

public Enumeration ServletConfig.getInitParameterNames()

这个方法返回值是字符串对象的枚举类型或空值 (如果没参数),经常用于程序的调试。

G e n e r i c S e r v l e t类也可以使得s e r v l e t s直接访问这个方法。

2. 服务器

s e r v l e t能了解服务器的大多数信息。它能知道主机名称,端口号,服务器软件及其他信息。

S e r v l e t还能将这些信息显示给客户端,以定制客户机基于特定服务器数据包的行为,甚至可以

明确要运行s e r v l e t的机器的行为。

( 1 ) 服务器相关信息

s e r v l e t通过四个方法得到服务器的信息:两个由传递给 s e r v l e t的S e r v l e t R e q u e s t对象调用,两

个从S e r v l e t C o n t e x t对象调用, S e r v l e t C o n t e x t包含s e r v l e t的运行环境。 通过使用方法g e t S e r v e r N a m e ()

和g e t S e r v e r P o r t() ,S e r v l e t能为具体请求分别获取服务器的名称和端口号:

public String ServletRequest.getServerName()

public int ServletRequest.getServerPort()

S e r v l e t C o n t e x t的G e t S e r v e r I n f o()和g e t A t t r i b u t e()方法提供服务器软件和属性的信息:

public String ServletContext.getServerInfo()

public Object ServletContext.getAttribute(String name)

(2) 锁定s e r v l e t到服务器

利用服务器信息可以完成许多特殊的功能,例如:写了一个s e r v l e t,而且不想让它随处运行,

或许你想出售,限制那些未授权的拷贝,或者想通过一个软件证书锁定 s e r v l e t到客户机上。另一

种情况可能是,开发人员为s e r v l e t编写了一个证书并且想确保它运行于防火墙后。这都不难做到,

因为s e r v l e t能及时地访问到服务器的相关信息。

3 客户端

对每个请求,s e r v l e t能获取客户机、要求认证的页面及实际用户的有关信息。这些信息可用

第二章 预 备 知 识计计37 下载于登录访问,收集用户个人资料或限制某些客户端的访问。

(1) 获取客户机信息

s e r v l e t可用g e t R e m o t e A d d r ( )和g e t R e m o t e H o s t ( )分别获取客户机的I P地址和主机名称:

public String ServletRequest.getRemoteAddr()

public String ServletRequest.getRemoteHost()

以上两种方法都返回字符串对象类型。这些信息来自于连接服务器和客户端的 s o c k e t端口,所以

远程地址和远程主机名有可能是代理服务器的地址和主机名称。远程地址可能是“1 9 2 . 2 6 . 8 0 . 11 8”,

而远程主机名可能是“d i s t . e n g r. s g i . c o m” 。

I n e t A d d r e s s . g e t B y N a m e ( )方法可以将I P地址和远程主机名称转化为j a v a . n e t . I n e t A d d r e s s对象:   

InetAddress remoteInetAddress = InetAddress.getByName(req.getRemoteAddr());

(2) 限制为只允许某些地区的机器访问

由于美国政府对好的加密技术出口的限制政策,在某些 We b站点上,下载允许的软件就得特

别谨慎。利用s e r v l e t的获取客户机信息的功能,可以很好地加强这种限制。这些 s e r v l e t能检查客

户机,并对那些来自美国和加拿大的机器提供下载链接。

对于商业应用,可以确定让一个S e r v l e t只对来自企业内部网的客户机服务,从而保证安全。

(3) 获取用户相关信息

如果要对Web pages作更进一步的限制,而不是根据地域限制访问,该怎么做呢?比如,发

布的在线杂志,只想让已定购的用户可以访问。读者也许会说,这好办,我早都能做,不一定

非得用s e r v l e t。

是的,几乎每种H T T P服务器都内嵌了这种功能,可以限制特定用户访问所有或部分网页。

怎样设定限制依你所使用的服务器不同而有差异,这里我们给出它们的共同工作机理。浏览器

第一次试图访问某个页面时,服务器会给浏览器一个要求身份验证的响应,浏览器收到这个响

应后,会弹出一个要求输入用户名和密码的对话框。

用户输入信息后,浏览器会试图再一次访问该页,但这次请求信息中附加了用户名称和密

码信息。服务器接受用户名/密码对后,就会处理此请求;如果相反,服务器不接受此用户名 /密

码对,浏览器的访问再一次被拒绝,用户必须重新输入。

那么,s e r v l e t是怎样处理的呢?当访问受限制的 s e r v l e t时,s e r v l e t可以调用g e t R e m o t e U s e r ( )

方法,获取服务器认可的用户名称:

public String HttpServletRequest.getRemoteUser()

S e r v l e t也可以用g e t A u t h Ty p e()方法获取认证类型:

public String HttpServletRequest.getAuthType()

这个方法返回所用的认证类型或可空值 (如果未加限制),最常使用的认证类型是“ B A S I C”

和“D I G E S T” 。

(4) 个性化的欢迎信息

一个简单的调用g e t R e m o t e U s e r ( )方法的s e r v l e t,可以通过称呼用户名称向他问好,并记住他

上次登录的时间。但是需要注意的是,这种方法仅仅适用于授权用户。对于未授权用户,可以

使用S e s s i o n。

38计计第一部分 JSP    入 门

下载4. 请求

前面讲述了 s e r v l e t如何获取服务器和客户机的相关信息,现在要学习真正重要的内容:

s e r v l e t如何知道客户请求什么。

(1) 请求参数

每个对s e r v l e t的访问都可以有许多与之相关的参数,这些参数都是典型的名称 /值对,用于

告诉s e r v l e t在处理请求时所需的额外信息。千万注意别把这里的参数与前面提到的与 s e r v l e t自身

相关的参数搞混。

幸运的是,即使s e r v l e t要获取许多参数,获取每个参数的方法也都一样,即 g e t P a r a m e t e r ( )和

g e t P a r a m e t e r Va l u e s ( )方法:

public String ServletRequest.getParameter(String name)

public String[] ServletRequest.getParameterValues(String name)

g e t P a r a m e t e r ( )以字符串形式返回命名的参数,如果没指定参数则返回空值,返回值必须保

证是正常的编码形式。如果此参数有多个值,那么这个值是与服务器相关的,这种情况下应该

用g e t P a r a m e t e r Va l u e s ( )方法,这个方法以字符对象数组的形式返回相应参数的所有值,如果没

指定,当然为空值。返回的每个值在数组中占一个单位长度。

除了能获取参数值之外,s e r v l e t还能用g e t P a r a m e t e r N a m e s ( )获取参数名称:

public Enumeration ServletRequest.getParameterNames()

这个方法以字符串枚举类型返回参数名称,或者在没有参数时返回空值。这个方法经常用

于程序的调试。

最后,s e r v l e t还能用g e t Q u e r y S t r i n g ( )方法获取请求的二进制字串:

public String ServletRequest.getQueryString()

这个方法返回请求的二进制字串(已编码的G E T参数信息),如果没有请求字串,则返回空值。

这些底层数据很少用来处理表单数据。

(2) 发布许可证密钥

如果现在准备编写一个给特定主机和端口号发布 K e y e d S e r v e r L o c k许可证密钥的s e r v l e t,从

s e r v l e t获取的密钥可用于解锁K e y e d S e r v e r L o c k的s e r v l e t。那么,怎么知道s e r v l e t所要解锁的主机

名和端口号呢?当然是请求参数。

(3) 路径信息

除参数信息外,H T T P请求还能包括“附加路径信息”或“虚拟路径”等。通常,附加路径

信息是用于指明s e r v l e t要用到的文件在服务器上的路径,一般用 H T T P请求的U R L形式表示,可

能像这样:

h t t p:/ / s e r v e r:p o r t / s e r v l e t / Vi e w F i l e / i n d e x . h t m l

这将激活ViewFile servlet,同时传递“i n d e x . h t m l”作为附加路径信息。S e r v l e t可以访问这

个路径信息,还能将字串“i n d e x . h t m l”转化为文件i n d e x . h t m l的真实路径。什么是“/ i n d e x . h t m l”

的真实路径呢?它是指当客户直接请求“/ i n d e x . h t m l”文件时,服务器返回的完整文件系统路径。

可能是d o c u m e n t _ r o o t / i n d e x . h t m l,当然服务器也可能用别名将它改变了。

除了用明确的U R L形式指定外,附加信息也可写成H T M L表单中A C T I O N参数的形式:

第二章 预 备 知 识计计39 下载<FORM METHOD=GET 

ACTION="/servlet/Dictionary/dict/definitions.txt">

word to look up: <INPUT TYPE=TEXT NAME="word"><P>

<INPUT TYPE=SUBMIT><P>

</FORM>

表单激活Dictionary servlet处理请求任务,同时传递附加路径信息“ d i c t / d e f i n i t i o n s . t x t” 。

s e r v l e t会用单词d e f i n i t i o n s查找d e f i n i t i o n s . t x t文件,如果客户请求“ / d i c t / d e f i n i t i o n s . t x t”文件,

同时在s e r v e r _ r o o t / p u b l l i c _ h t m l / d i c t / d e f i n i t i o n s . t x t也存在,客户将会看到相同的文件。

1 ) 获取路径信息。

s e r v l e t可用g e t P a t h I n f o ( )方法获取附加路径信息:

public String HttpServletRequest.getPathInfo()

这个方法返回与请求相关的附加路径信息,或者在没给定时,返回空值。 S e r v l e t通常需要

知道给定文件的真实文件系统路径,这样就有了 g e t P a t h Tr a n s l a t e d ( )方法:

public String HttpServletRequest.getPathTranslated()

这个方法返回已经转化为真实文件路径的附加路径信息,或者在没有附加路径信息时返回空

值。返回的路径未必要指向已存在的文件和目录,已转化的路径可能是: “C:/ J a v a We b S e r v e r 1 . 1 . 1

/public_html/dict/definitions.txt” 。

2 ) 特别的路径转换。

有时,s e r v l e t需要在附加路径信息中没有的路径,就得用 g e t R e a l P a t h ( )方法来完成此项任务:

public String ServletRequest.getRealPath(String path)

这个方法返回任何给定“虚拟路径”的真实路径,或者返回空值。如果给定路径是“/” ,这个

方法返回服务器文档的根目录;如果给定路径是g e t P a t h I n f o ( ),返回的路径与g e t P a t h Tr a n s l a t e d ( )方

法的返回值相同。Generic servlets和HTTP servlets都可使用这个方法,CGI中没有与之相关的函数。

3 ) 获取M I M E类型。

s e r v l e t知道文件路径后,还往往需要知道文件类型,可使用g e t M i m e Ty p e ( )方法来做这项工作:

public String ServletContext.getMimeType(String file)

这个方法返回指定文件的M I M E类型,或者在不知道的情况下返回空值。有时,在文件不存

在时也返回“ t e x t / p l a i n” 。常见的文件类型有: “t e x t / h t m l” , “t e x t / p l a i n” , “i m a g e / g i f”和

“i m a g e / j p e g” 。

下面这个语句代码可获取附加路径信息的M I M E类型:

String type = getServletContext().getMimeType(req.getPathTranslated())

(4) 服务文件

许多应用服务器,例如We b L o g i,利用s e r v l e t来处理每个请求,这不仅是 s e r v l e t处理能力上

的优势,而且,这为服务器的模块化设计带来了极大的便利。比如,所有的文件都由 c o m . s u n .

s e r v e r.http.FileServlet servlet提供服务,此s e r v l e t在f i l e下注册,并负责处理“/”别名(是请求的

缺省文件) 。

(5) 决定被请求的内容

40计计第一部分 JSP    入 门

下载s e r v l e t能用几种方法获取客户请求的确切文件。毕竟,最根部的 s e r v l e t总假定为直接请求的

目标,在一个很长的s e r v l e t链中,每个s e r v l e t只能有一个连接。

没有方法用于直接返回客户用于请求的原始 U R L,j a v a x . s e r v l e t . h t t p . H t t p U t i l s类的

g e t R e q u e s t U R L ( )方法能完成类似的工作:

public static StringBuffer HttpUtils.getRequestURL(HttpServletRequest req)

这个方法基于H t t p S e r v l e t R e q u e s t对象的变量信息,重构请求的 U R L,它返回S t r i n g B u ff e r类

型,包含构架(像H T T P) 、服务器名、端口号和额外路径信息。重构后的 URL 应该与客户请求

的U R L非常相像,它们之间的差别非常细微(比如空格形式在客户端用 % 2 0表示,而在服务器

端是"+") 。由于这个方法返回S t r i n g B u ff e r类型,所以U R L能被有效地修改(比如,附加些查

询参数) 。这个方法经常用于创建重定向消息和报告错误。

大多数情况下,s e r v l e t并不真正需要请求的U R L,而需要的是U R I,它是由方法g e t R e q u e s t U R I ( )

返回的:

public String HttpServletRequest.getRequestURI()

这个方法返回统一资源标示符( U R I) ,对正常的HTTP servlet来说,一个U R I可以看成是

U R L减去构架、主机名、端口号和请求字串,但包含一些额外路径信息。

(6) 请求机理

除了知道请求的内容外, s e r v l e t还有一种方法用于获取如何请求的信息。 G e t S c h e m e ( )方法

用于返回请求的构架:

public String ServletRequest.getScheme()

例如“h t t p”, “h t t p s”,和“f t p” ,还有J a v a特有的“j d b c”和“r m i” 。虽然C G I也有包含构

架的变量S E RV E R _ U R L,但没有与g e t S c h e m e ( )相应的函数。对HTTP servlet来说,这个方法表

明请求是通过使用S S L安全连接(用“h t t t p s”表示) ,还是非安全连接(用“h t t p”表示) 。

g e t P r o t o c o l ( )方法返回用于请求的协议和版本号:

public String ServletRequest.getProtocol()

协议和版本号用斜线隔开。如果不能决定协议类型,则返回空值。对 HTTP servlet来说,协

议通常是“v H T T P / 1 . 0 v”或“v H T T P / 1 . 1”, HTTP servlet能用协议版本决定客户端是否可使用

HTTP 1.1的新特性。

要获取请求所用的方法,s e r v l e t调用g e t M e t h o d ( ):

public String HttpServletRequest.getMethod()

这个函数返回用于请求的 H T T P方法,包括“G E T” , “P O S T”和“H E A D” ,H t t p S e r v l e t的

实现函数s e r v i c e ( )用这个方法分派请求任务。

(7) 请求头

H T T P请求和响应有许多与HTTP 头相关的内容。这些头提供了一些与请求(或响应)有关

的额外信息。HTTP 1.0协议提供了十几种头,HTTP 1.1则更多。对头的完整讨论超出了本书的

范围,这里描述s e r v l e t最常访问的头。

s e r v l e t在执行请求时很少需要读HTTP  头,许多与请求相关的头信息都由服务器处理了。这

第二章 预 备 知 识计计41 下载里举一个服务器如何限制对某些文档的访问的例子,服务器使用 H T T P头,而s e r v l e t并不了解其

中细节。当服务器接到访问受限页面的请求时,它会检查带有适当认证信息头的请求,这个头

应该包含合法的用户名和密码,如果没有,服务器发出包含 W W W- A u t h e n t i c a t e的头,告诉浏览

器对资源的访问被拒绝,如果客户发送的请求包含正确的认证头,服务器会授权访问并允许激

活的s e r v l e t通过g e t R e m o t e U s e r ( )方法获取用户名称。

1) 访问头值。

H T T P头值可以通过 H t t p S e r v l e t R e q u e s t对象访问。使用 g e t H e a d e r ( )、g e t D a t a H e a d e r ( )或

g e t I n t H e a d e r ( )方法, 它们分别返回S t r i n g类型、long 类型和i n t类型数据:

public  String HttpServletRequest.getHeader(String name)

public  long HttpServletRequest.getDateHeader(String name)

public int HttpServletRequest.getIntHeader(String name)

g e t H e a d e r ( )方法以S t r i n g类型返回指定头的值,如果头未作为请求的组成部分,则返回空值,

所有类型的头都可以用这种方法获取。

g e t D a t e H e a d e r ( )返回l o n g类型的值,表示日期,如果头未作为请求的组成部分,则返回- 1。

当被调用的头值不能转化为 D a t e时,此方法会抛出一个 I l l e g a l A rg u m e n t E x c e p t i o n。这个方法在

处理l a s t - M o d i f e d和I f - M o d i f i e d - S i n c e的头时非常有用。

G e t I n t H e a d e r ( )返回头的整型值或-1(如果未作为请求的组成部分被发送) ,当被调用的头不

能转化为整型时,这个方法抛出N u m b e r F o r m a t E x c e p t i o n异常。

如果能使用g e t H e a d e r N a m e s ( )访问,s e r v l e t还能获得所有的头名称:

public Enumeration HttpServletRequest.getHeaderNames()

这个方法以S t r i n g对象的枚举类型返回所有头的名称。如果没有头,则返回空的枚举类型。

2) servlet链中的头信息。

s e r v l e t链加了一个有趣的处理 s e r v l e t头信息的环,不像其他的 s e r v l e t s,处于链中或链末的

s e r v l e t不是从客户请求中读取头信息值,而是从前一个 s e r v l e t的响应信息中读取头信息值。

这种处理方法的强有力和灵活性来自于这样一个事实: s e r v l e t能智能地处理前一个s e r v l e t的

输出,不仅在内容方面,而且在头信息值方面。比如,它能向响应信息中加些额外的头信息,

或改变已有的头信息。它甚至还能禁止头信息。

但是这种强大的功能是要负责任的:除非 s e r v l e t明确地读取前一个s e r v l e t的响应头信息,并

作为它自己的响应信息的一部分加以发送,否则此头信息将不被发送,客户端就看不到此信息。

正常的链中s e r v l e t总是会传递前一个s e r v l e t的头,除非有特殊原因而需要处理别的什么。

(8) 输入流

每个由s e r v l e t处理的请求都有一个与之相关的输入流,就像 s e r v l e t将相关的r e s p o n s e对象写

到P r i n t Wr i t e r或O u t p u t S t r e a m中一样,s e r v l e t也能从R e a d e r或I n p u t S t r e a m中读取与请求相关的信

息。从输入流中读取的数据可以为任何数据类型和任意长度,输入流有三个作用:

1) 在s e r v l e t链中,把前一个s e r v l e t的响应信息向下传递;

2 ) 将与P O S T请求相关的内容传递给HTTP servlet;

3) 把客户端发来的二进制信息传递给non-HTTP servlet。

42计计第一部分 JSP    入 门

下载要读取输入流中的字符数据,应该使用g e t R e a d e r ( )方法,并返回B u ff e r d R e a d e r的类对象:

public BufferdReader ServletRequest.getReader() throws IOException

使用B u ff e r e d R e a d e r作为返回的数据类型的优点是,它能在各种字符集间正确地转换。如果

g e t I n p u t S t r e a m ( )在同样的请求之前被调用,这个方法会抛出 I l l e g a l S t a t e E x c e p t i o n异常,如果输

入字符不被支持或是未知字符,则抛出U n s u p p o r t e d E n c o d i n g E x c e p t i o n异常。

要从输入流中读取二进制数据,就得使用 g e t I n p u t S t r e a m ( )方法,并返回S e r v l e t I n p u t S t r e a m

类型:

public ServletInputStream ServletRequest.getInputStream()  throws  IOException

S e r v l e t I n p u t S t r e a m是I n p u t S t r e a m的直接子类,可以当作正常的 I n p u t S t r e a m来处理,具有有

效的一次一行地读取数据的能力。如果在同一个请求之前调用 g e t R e a d e r ( ),这个方法会抛出

I l l e g a l S t a t e E x c e p t i o n异常。一旦有了S e r v l e t I n p u t S t r e a m,就可以用r e a d L i n e ( )进行行读取:

public int ServletInputStream.readLine(byte b[],int off,int len)

throws IOException

这个方法从输入流中将 b y t e s读入字节数组b中,开始处由o ff给出,遇到‘/ n’时或读够l e n

字节时结束读取。结束符“/ n”也读入缓存。这个方法返回已读取的字节数,或到达输入流的末

尾时返回- 1。

S e r v l e t使用g e t C o n t e n t Ty p e ( )和g e t C o n t e n t L e n g t h ( )分别获取经过输入流发送的内容类型和数

据长度:

public String ServletRequest.getContentType()

public int ServletRequest.getContentLength()

g e t C o n t e n t Ty p e ( )方法返回经过输入流的发送内容类型,或返回空值(如果类型不明,比如

没有数据);g e t C o n t e n t L e n g t h ( )返回按字节计算的长度,如果类型不明则返回- 1。

1 ) 用输入流构建s e r v l e t链。

链中的s e r v l e t从前一个s e r v l e t中通过输入流获得响应信息。

2 ) 输入流处理P O S T请求。

当s e r v l e t处理P O S T请求时,使用输入流来访问 P O S T数据的情况是极少见的。典型地,

P O S T数据只不过是参数信息,s e r v l e t可以很方便地用g e t P a r a m e t e r ( )方法获取它。S e r v l e t可以检

查输入流的类型来识别P O S T请求的类型,如果是a p p l i c a t i o n / x - w w w - f o r m - u r l e n c o d e d类型,数据

可由g e t P a r a m e t e r ( )方法或相似的方法获取。

S e r v l e t应在调用g e t P a r a m e t e r ( )方法之前调用g e t C o n t e n t L e n g t h ( ),以免被拒绝访问。恶意客

户可能在发送P O S T请求时,发送不合理的巨大的数据,企图在s e r v l e t调用g e t P a r a m e t e r ( )方法时,

把服务器的速度降到最慢。 S e r v l e t可以用g e t C o n t e n t L e n g t h ( )来验证数据长度的合理性,或限定

小于4 k作为预防措施。

3 ) 用输入流接收文件。

s e r v l e t可以使用输入流来接收上传的文件,在讲解之前,需提醒很重要的一点是:上传文件

是试验性的,并且不是所有的浏览器都支持。 Netscape Navigator 从3 . 0,微软从Internet Exploer

4 . 0才开始支持文件上传。

第二章 预 备 知 识计计43 下载简单地说,任何数量的文件和参数都可以在单个 P O S T请求中作为表单数据被传送, P O S T

请求格式与标准a p p l i c a t i o n / x - w w w - f o r m - u r l e n c o d e d的表单格式不同,所以有必要将类型设为

m u l t i p a r t / f o r m - d a t a。

文件上载的客户端程序的编写相当简单,下面这个 H T M L将发送一个表单,询问用户名称和

要上载的文件。注意E N C T Y P E属性和F I L E输入类型的使用。

<FORM ACTION = "/servlet/UploadTest" ENCTYPE="multipart/form-data"

METHOD=POST>

What is your name? <INPUT TYPE=TEXT NAME=submitter> <BR>

Which file do you want to upload? <INPUT TYPE=FILE NAME=file> <BR>

<INPUT TYPE=SUBMIT>

</FORM>

具体如何实现文件上传,请见本书第11章。

(9) 额外属性

有时s e r v l e t需要了解请求的其他信息,并且这些信息用前面提到的方法无法获取,这时就得

使出最后一招,即g e t A t t r i b u t e()方法。还记得S e r v l e t C o n t e n t有个g e t A t t r i b u t e()方法是如何

返回特定服务器的属性的吗?S e r v l e t R e q u e s t也有一个g e t A t t r i b u t e()方法:

public object ServletRequest.getAttribute(String name)

这个方法返回指定请求的服务器属性,如果服务器不支持指定请求的属性则返回空值。这

个方法允许服务器为s e r v l e t提供有关请求的定制信息。例如,Java Web Server有三个可获得的属

性:j a v a x . n e t . s s l . c i p h e r s u i t e、j a v a . n e t . s s l . p e e r _ c e r t i f i c a t e s和j a v a x . n e t . s s l . s e s s i o n。运行在J a v a

Web Server上的s e r v l e t可以窥探客户S S L连接的这些属性。

2.3.3   传送H T M L信息

本节首先讲解从s e r v l e t如何返回一个标准的HTML response,然后讲解如何建立客户端的持

久连接以降低返回r e s p o n s e的开销。最后还要探讨一下在处理 H T M L和H T T P时的一些额外的东

西,包括用支持类来对象化H T M L输出,返回错误和其他状态码,发送定制的头信息,重定向请

求,使用客户牵引,客户掉线检测和向服务器日志中写数据等。

1. response的结构

HTTP servlet能为客户返回三种信息:一个状态码、任意数量的 H T T P头和应答信息。状态

码是个整数,就像你想像的那样,它描述了应答状态。状态码能表明成功和失败,或告诉客户

机采取下一步动作完成请求过程。数字状态码通常伴有“原因词” ,以人们容易看懂的方式来描

述状态。通常状态码在后台工作并由浏览器软件解释。有时,尤其是当出错时,浏览器向用户

显示状态码。最常见的状态码可能要数“404 Not Found” ,当服务器不能定位U R L时,它就会发

出这个状态码。

响应体是响应信息的主要内容,对 H T M L页来说,响应体就是H T M L本身。对图片来说,响

应体是构成图片的字节。响应体可以是任何类型和任意长度;客户端通过解释响应信息中的

H T T P头知道该接收什么。

普通的s e r v l e t比HTTP servlet简单——它只向用户返回应响体。然而,对 G e n e r i c S e r v l e t子类

44计计第一部分 JSP    入 门

下载来说,用A P I将一个响应体分成许多精细的部分是可能的,好像返回多条目的感觉。事实上,这

就是HTTP servlet要做的工作。在底层,服务器将响应以字节流的形式发送给客户端,任何设置

状态码或头的方法都是在这个基础上的抽象。

明白这点很重要,因为即使 s e r v l e t程序员不必了解 H T T P协议的细节,协议确实会影像

s e r v l e t调用方法的顺序。特别是, H T T P协议规定状态码和头必须在响应体之前发送。因此,

s e r v l e t要注意发送任何响应体之前总是先设置状态码和头。有些服务器,包括 Java Web Server,

内部缓存了一些s e r v l e t响应体(通常大约是4 K)——这允许在即使s e r v l e t写了一个较短的响应体

之后,可以有一定自由度地设置状态码和头。然而,这些行为都是服务器相关的,一名明智的

s e r v l e t编程人员,应该忘掉这一切。

2. 发送标准的响应信息

S e r v l e t R e s p o n s e的s e t C o n t e n t Ty p e ( )方法可以将响应的内容设置为指定的M I M E类型。

public void ServletResponse.setContentType(String type)

在HTTP servlet中,这个方法用于设置C o n t e n t - Type HTTP头。

G e t Wr i t e r ( )方法返回一个P r i n t Wr i t e r对象,用于写基于字符的响应数据:

public PrintWriter ServletResponse.getWriter()  throws IOException

这个w r i t e r根据内容类型中给定的字符集进行字符编码,如果没指定字符集(这是常事) ,

w r i t e r将用I S O - 8 8 5 9 - 1进行编码,I S O - 8 8 5 9 8 - 1主要是用于西欧文字。字符集在后面的章节中会有

所提及,所以现在你只要记住在得到 P r i n t Wr i t e r之前,总是先设置内容的类型。如果 g e t O u t p u t

S t r e a m ( )已被这个响应调用,这个方法将抛出 I l l e g a l S t a t e E x c e p t i o n异常;如果输出流的编码形式

不被支持或不明,则抛出U n s u p p o r t e d E n c o d i n g E x c e p t i o n异常。

除了用P r i n t Wr i t e r返回响应外,s e r v l e t还能用j a v a . i o . O u t p u t S t r e a m的特殊子类来写二进制数

据,这就是S e r v l e t O u t p u t S t r e a m类,它在j a v a . s e r v l e t中定义。可以用g e t O u t p u t S t r e a m ( )方法得到

一个S e r v l e t O u t p u t S t r e a m对象:

public ServletOutputStream ServletResponse.getOutputStream()  throws  IOException

这个方法返回一个S e r v l e t O u t p u t S t r e a m类对象,用于写二进制(一次一个字节)响应数据。不进

行任何编码。如果已为这个响应调用了g e t Wr i t e r ( ),这个方法将返回一个I l l e g a l S t a t e E x c e p t i o n异常。

S e r v l e t O u t p u t S t r e a m很像标准的Java PrintStream类,在Servlet API v1.0中,这个类用于所有

的s e r v l e t的输出,既可是文本类型,又可是二进制类型。在 Servlet API v2.0中,它仅用于处理二

进制的输出。由于是O u t p u t S t r e a m的直接子类,它拥有O u t p u t S t r e a m的w r i t e ( )、f l u s h ( )和c l o s e ( )

方法。为解决这个问题,它加了自己的 p r i n t ( )、p r i n t l n ( )方法,用于写大多数的J a v a原始类型的

数据。S e r v l e t O u t p u t S t r e a m接口与P r i n t S t r e a m接口的不同是:S e r v l e t O u t p u t S t r e a m的p r i n t()和

p r i n t l n()方法不能明确地直接显示O b j e c t或c h a r [ ]类型的数据。

3 使用持续连接

持续连接(有时又称作" k e e p - a l i v e"连接)能优化s e r v l e t向客户返回内容的方式。要明白

它的优化机理,必须首先弄懂 H T T P连接是如何工作的。下面将对这里面的基本概念做一些浅显

的解释。

当客户,比如浏览器,想从服务器请求一个 We b文档时,首先会与服务器建立一个 s o c k e t的

第二章 预 备 知 识计计45 下载连接。通过这个连接,客户端可以发出请求和接收服务器响应。客户端发送空行表示完成请求,

反过来,服务器通过关闭s o c k e t连接表明响应已完成。

就这么简单。但是如果收到的网页中含有 < I M G >标签或< A P P L E T >标签,要求客户机从服

务器接收更多的内容,该怎么办呢?那么又得另起一个 s o c k e t,如果一个网页中包含1 0个图片和

一个由2 5个类组成的a p p l e t,就需要3 6个连接来传送此页(当然,现在可以使用 J A R文件打包这

些类而减少连接数目) 。难怪有人戏称W W W为Word Wide Wa i t!。

一个较好的方法是同一个s o c k e t连接接收更多的网页内容,有时也把这种技术称作持续连接。

持续连接的关键是客户端与服务器必须在服务器的响应结束处与客户端开始下一个连接的地方

达成一致。它们可能用一个空行作为记号,但如果响应信息本身包含一个空行又怎么办呢?持

续连接的工作方式是,服务器在响应体中设置C o n t e n t - L e n g t h头,告诉客户端这个响应体有多大。

客户端就知道接收完这个响应体之后,才控制下一个 s o c k e t连接。

大多数服务器都能处理它所服务文件的 C o n t e n t - L e n g t h头,但对不同的s e r v l e t,处理方式不

一样。S e r v l e t可以使用s e t C o n t e n t L e n g t h ( )方法来处理动态内容的持续连接:

public void ServletResponse.setContentLength(int len)

这个方法返回服务器响应内容的长度,在 HTTP servlet中,这个方法设定 HTTP Content-

L e n g t h头。注意这个方法的使用是可选的,然而,如果使用它, s e r v l e t将会充分利用持续连接的

优点,客户端也能在下载时精确显示过程控制。

调用s e t C o n t e n t L e n g t h()方法时,有两点要注意: s e r v l e t必须在发送响应信息之前调用此

方法,给定的长度必须精确。哪怕只有一个字节的误差,都可能出问题。这听起来似乎很难做

到,实际上,s e r v l e t用B y t e A r r a y O u t p u t S t r e a m类型来缓存输出。

s e r v l e t不是将响应信息写到由 g e t Wr i t e r ( )返回的 P r i n t Wr i t e r,而是写到建立在

B y t e A r r a y O u t p u t S t r e a m基础上的P r i n t Wr i t e r中,这个数组会随s e r v l e t的输出而增长。当s e r v l e t准备

退出时,它能设置内容的长度为缓存尺寸,并将内容发送到客户端缓存。注意,字节是基于

S e r v l e t O u t p u t S t r e a m字节对象发送的。做这么点简单的修改,s e r v l e t就可能利用持续连接的优点。

持续连接是要付出代价的,这一点很重要。缓存所有的输出和成批发送数据需要更多的内

存,还可能延迟客户端开始接收数据的时间点。对响应较短的 s e r v l e t来说,可以接受;但对响应

较长的s e r v l e t,考虑内存开销和延迟的代价,可能还不如建立几个连接。

还要注意一点,并不是所有的s e r v l e t都支持持续连接。就是说,设定 s e r v l e t响应的内容长度

是好的选择,这个长度信息被支持持续连接的服务器使用,而被其他的服务器忽略。

4. 生成H T M L

H T M L生成包为s e r v l e t提供了一系列的类,这些类抽象了 H T M L的许多细节,尤其是H T M L

的标签。抽象的程度取决于所使用的包:有些只有极少的 H T M L标签,留下一些基本的细节(比

如打开和关闭H T M L标签)给编程人员。利用这类包类似于手工写 H T M L,在这里就不讨论了。

另一类包是很好地抽象了H T M L的具体内容,同时把H T M L作为J a v a对象的集合。一个网页可以

看作是一个对象,它可以包含其他 H T M L对象(比如列表和表格) ,还能包含更多的H T M L对象

(比如列表项和表格单元) 。这种面向对象的方法可以极大地简化 H T M L的生成工作,并且使得

s e r v l e t更容易编写、维护,有时则更高效。

46计计第一部分 JSP    入 门

下载生成Hello Wo r l d

下例是一个普通的Hello World Servlet,作为一个最简单的S e r v l e t,相信读者通过前面的学

习可以理解它。

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

/**Hello World! Servlet Class */

public class HelloWorld extends HttpServlet {

/**doGet方法的重载,用于处理客户端的GET请求*/

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws IOException, ServletException {

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("<html>");

out.println("<body>");

out.println("<head>");

out.println("<title>Hello World!</title>");

out.println("</head>");

out.println("<body>");

out.println("<h1>Hello World!</h1>");

out.println("</body>");

out.println("</html>");

}

}

5. 状态码

目前我们的例子还没有设置 H T T P响应的状态码。如果s e r v l e t没有特别指定状态码,服务器

会将其设为缺省值200 “O K”状态码。这在成功返回标准响应时非常便利。然而,利用状态码,

s e r v l e t可以对响应信息做更多的工作,比如,它可以重定向请求和报告错误等。

最常使用的状态码在H t t p S e r v l e t R e s p o n s e类中被定义为助记常量(public final static int) 。表

2 - 1列出了其中的很少几个,全部的状态码可以在第 6章中查到。

表2-1   HTTP状态码

助 记 常 量 码 值 默 认 消 息 意 义

S C _ O K 2 0 0 O K 客客户请求成功,服务器的响应信

息包含请求的数据。这是缺省的状

态码

S C _ N O _ C O N T E N T 2 0 4 No Content 客请求成功,但没有新的响应体返

回。接收到这个状态码的浏览器应

该保留它们当前 r的文档视图。当

s e r v l e t从表单中收集数据,希望浏览

器保留表单,同时避免 "D o c u m e n t

contains no data"错误信息时,这个

状态码很有用

第二章 预 备 知 识计计47 下载(续)

助 记 常 量 码 值 默 认 消 息 意 义

S C _ M O V E D _ P E R M A N E N T LY 3 0 1 Moved Perma-nently 客被访问的资源被永久的到一个新

的位置,在将来的请求中应使用新

的U R L。新的U R L由L o c a t i o n头给

出,大多数的浏览器会自动访问新

的路径

SC_MOVED _TEMPORARILY 3 0 2 Moved Tempo- rarily 客被访问的资源被暂时移到一个新

的位置,在将来的请求中仍使用新

的U R L。新的U R L由L o c a t i o n头给

出,大多数的浏览器会自动访问新

的路径

SC_ UNAUTHORIZED 401   U n a u t h o r i e d 客请求没有正确的地认证。用于

W W W- A u t h e n t i c a t e和A u t h e n t i c a t i o n

连接

S C _ N O T _ F O U N D 4 0 4 Not Found 客被请求的资源没找到或不可访问

SC_INTERNAL_ SERV E R _ E R R O R 500   客Internal Server Error 客服务器内部错误,妨碍了请求过

程的正常进行

SC_NOT_ IMPLEMENTED 5 0 1 Not Implemented 客服务器不支持用于完成请求的函

S C _ S E RV I C E _ U N AVA I L A B L E 5 0 3 Service Unavail-able 客服务器暂时不可访问,将来可恢

复。如果服务器知道何时可被访问,

它会提供R e t r y - A f t e r头

设定状态码

s e r v l e t可用S e t S t a t u s ( )方法设定响应状态码:

public void HttpServletResponse.setStatus(int sc)

public void HttpServletResponse.setStatus(int sc, String sm)

这两个方法都可将 H T T P的状态码设为指定的值,这个状态码可为一个数字,或为在

H t t p S e r v l e t R e s p o n s e中定义的S C _ X X X码。带一个参数的方法,原因部分被设置为缺省的消息;

带两个参数的方法,原因部分可指定不同的消息。记住, s e t S t a t u s()方法应在s e r v l e t返回任何

响应体之前调用。

如果s e r v l e t在处理请求时将状态码设置报错,则它调用 s e n d E r r o r()方法,而不是

s e n d S t a t u s()方法:

public void HttpServletResponse.setError(int sc)

public void HttpServletResponse.setError(int sc, String sm)

s e r v l e t对s e n d E r r o r()和s e n d S t a t u s的处理过程不同。当带两个参数的方法被调用时,状态

码的消息参数可被替代的原因取代,或者它被直接用于响应体中,这取决于服务器的执行。

6. HTTP头

S e r v l e t能设定H T T P头,提供与响应相关的额外信息。表 2 - 2列出了一些s e r v l e t最常使用的

H T P P头。

48计计第一部分 JSP    入 门

下载表2-2   HTTP响应头

C a c h e - C o n t r o l 指指定任何缓存系统对文档的操作。最常用的值有: n o - c a c h e(表明此文档不需缓存) ,

n o - s t o r e(表明此文档不必缓存,甚至不要保存在代理服务器中,通常是敏感内容) ,

m a x - a g e = s e c o n d s(表明文档到过时的时间长度) 。这个头从H T T P 1 . 1开始引入

P r a g m a 指H T T P 1 . 0中的等价头是C a c h e - c o n t r o l,并且只有一个可能的值n o - c a c h e

C o n n e c t i o n 指用于表明服务器是否愿意为客户维持持续连接。如果愿意,它的值设为 k e e p - a l i v e;

否则设为c l o s e。大多数的服务器代表它们的 s e r v l e t处理这个头,当s e r v l e t设置C o n t e n t -

L e n g t h头时,服务器就自动将C o n n e c t i o n的值设为k e e p - a l i v e

R e t r y - A f t e r 指指定服务器可再次处理请求的时间,与 S C _ S E RV I C E _ U N AVA I L A B L E状态码一起使

用。它的值要么是代表时间(秒)的整数,要么是代表实际时间的日期子串。

E x p i r e s 指指定何时文档可能改变,或它的信息将变成非法。它也意味着,在这个时间之前文档

不可能变化

L o c a t i o n 指指定文档的新路径,通常与 S C _ C R E ATED, SC_MOVED_PREMANENTLY和

S C _ M O V E D _ T E M P O R A R I LY等状态码一起使用。它的值必须是完整的 U R L(包含

"h t t p:/ /")

W W W- A u t h e n t i c a t e 指指定由访问请求URL 的客户要求的认证体系和认证范围。与 S C _ U N AT H O R I Z E D状

态码一起使用

C o n t e n t - E n c o d i n g 指指定用于编码响应体的体系。可能的值有: g z i p(或x - g z i p) 、c o m p r e s s(或x -

c o m p r e s s) 。多编码体系应按应用于数据的使用顺序用逗号隔开

(1) 设定H T T P头

H t t p S e r v l e t R e s p o n s e类提供了一系列的方法,帮助 s e r v l e t设置H T T P响应头信息。可使用

s e t H e a d e r()方法设置头值:

public void HttpServletResponse.setHeader(String name , String value)

这个方法将指定头的值设置为 S t r i n g。这个头名称是不变的,对所有的方法都一样。如果头

的值已经设定,新值将覆盖前一值。头的所有类型都可通过这个方法来设定。

如果需要为头值定义一个日期戳,可使用s e t D a t e H e a d e r()方法:

public void HttpServletResponse.setDateHeader(String name , long date)

这个方法把指定名称的头值设定为特定的日期和时间,并接收日期的 l o n g类型值(表示自

GMT 1970,1,1,0:0:0起的毫秒数) 。如果这个头已经设置过,新值将覆盖旧值。

最后,可以用s e t I n t H e a d e r()为头指定一个整型值:

public void HttpServletResponse.setIntHeader(String name , int value)

这个方法将头的值设定为整型,如果已经设置过,则新值覆盖旧值。

c o n t a i n s H e a d e r()方法提供了一种检查头是否存在的途径:

public boolean HttpServletResponse.containsHeader(String name)

如果头已被设置,这个方法返回t r u e,否则返回f a l s e。

另外,H T M L 3 . 2规范中,有另一种设置头值的方法:在 H T M L页中使用内嵌的 < M E TA

H T T P - E Q U I V >标签。

<META HTTP-EQUIV="name" CONTENT="value">

这个标签必须作为H T M L页的< H E A D >组成部分发送。这个技术对s e r v l e t没什么用,它主要

第二章 预 备 知 识计计49 下载用于静态文档,它们不必访问自己的头信息。

(2) 重定向请求

s e r v l e t使用状态码和头信息的另一个用途是重定向请求。这是通过给客户端发送另一个 U R L

指示来实现的。重定向通常用在:当文档移动时(给客户端发送新的路径) ,负载平衡(一个

U R L文档可能分布在几个不同的机器上) ,简单的随机文档(随机地选择目标路径) 。

真正的重定向发生在以下两行:

res.setStatus(res.SC_MOVED_TEMPORARILY);

res.setHeader("Location", site);

第一行设置状态码,表明需要重定向,第二行给出新的位置。为确保它们正常工作,必须

在发送任何输出之前调用这些方法。记住, H T T P协议在发送内容体之前发送状态码和头信息。

新站点的位置必须以绝对的U R L给出(例如,h t t p:/ / s e r v e r:p o r t / p a t h / f i l e . h t m l) ,否则,客户就

难以找到。

用s e n d R e d i r e c t()方法能很方便地将这两行简化为一行:

public void HttpServletResponse.sendRedirect(String location)  throws

IOException

这个方法重定向响应信息到新的位置,自动地设置状态码和L o c a t i o n的头信息,这两行就变成:

res.sendRedirect(site);

(3) 客户牵引

客户牵引与重定向类似,主要的区别在于:浏览器一直显示第一页的内容,并且在接收和

显示第二页之前等待一个指定的时间间隔。它之所以叫客户牵引,是因为由客户端拖曳下一页

的内容。

这有什么用呢?首先,可以在第二页下载之前,由第一页的内容告诉客户请求的内容已经

移动;其次,网页可按顺序接收,为制作慢速页面动画提供了可能。

客户牵引信息是使用Refresh HTTP 头发送给客户端的。这个头值指明在牵引下一页之前显

示当前页的秒数,也可选择地包含一个 U R L子串,从中下载文件。如果没给定 U R L,则使用相

同的U R L。下面这个语句告诉客户在显示当前内容3 s后,重新下载同一个s e r v l e t:

setHeader("Refresh","3");

这里还有一个告诉客户3 s后显示N e t s c a p e主页的语句:

setHeader("Refresh","3;URL=http://home.netscape.com");

7. 错误处理

s e r v l e t有时会出错,没关系,s e r v l e t必须得处理错误,有预料中的,也有始料未及的。错误

一旦发生,必须关心两件事:

1) 将对服务器的破坏降到最低。

2) 正确的通知客户端。

因为s e r v l e t是用J a v a编写的,所以它对服务器的破坏程度大大降低。服务器可以安全地嵌入

s e r v l e t(甚至嵌入它的进程中) ,就像We b浏览器能安全地嵌入下载的 a p p l e t s一样。这种安全性

是建立在J a v a的安全体系基础上的,包括保护内存使用,异常处理,安全管理机制等。 J a v a的内

50计计第一部分 JSP    入 门

下载存保护保证s e r v l e t不可能偶尔(或故意地)访问服务器的内部系统; J a v a的异常处理使服务器能

捕获由s e r v l e t引起的每一个异常,即使s e r v l e t偶尔被零除,或调用空对象的方法,服务器仍能够

继续工作;J a v a的安全管理机制为服务器提供了一种将信任的 s e r v l e t放于一个沙箱中的途径,限

制和防止它们故意破坏的能力。

应该谨慎的是,处于安全管理器沙箱之外运行的 s e r v l e t具有造成服务器破坏的能力,s e r v l e t

可能覆盖服务器的文件空间或甚至调用 S y s t e m . e x i t ( ),应该相信,被信任的s e r v l e t一般不会导致

服务器破坏,也极少会调用S y s t e m . e x i t ( )。

正确地向客户描述出现的问题,不仅仅是J a v a语言的事,还有许多需要考虑的因素:

1) 怎样告诉客户端?

s e r v l e t是给客户端发送普通的状态码错误页,还是发送错误的字面描述,抑或发送错误堆栈

的详细内容?

2) 怎样记录问题?

是保存到文件,写到服务器日志中,发送到客户端还是忽略?

3) 怎样恢复?

S e r v l e t是否继续处理下一个请求?或s e r v l e t崩溃,这意味着必须重新下载。

这些问题的答案取决于s e r v l e t和它的用途。怎样处理错误就由开发人员自己决定,但应确保

s e r v l e t在被请求时的可靠性和健壮性。接下来看看 s e r v l e t错误处理机制的全貌,可以有选择地用

它们来实现错误处理策略。

(1) 状态码

s e r v l e t最简单的报错方式是用s e n d E r r o r()方法发送恰当的4 0 0系列或5 0 0系列状态码。例如,

当s e r v l e t被请求一个不存在的文件时,它可以返回 S C _ N O T _ F O U N D;如果请求的工作超出了它

的能力范围,它可以返回 S C _ N O T _ I M P L E M E N T E D。当发生内部异常时,它可以返回

S C _ I N T E R N A L _ S E RV E R _ E R R O R。

通过使用状态码,s e r v l e t为服务器提供了一个给响应信息特殊处理的机会。例如,有些服务

器如Java Web Server,可以用与服务器相关的错误信息取代 s e r v l e t的响应信息;如果错误应由

s e r v l e t向客户提供解释,它可以用s e t S t a t u s()设置状态码,同时发出相应的响应体,这个响应

体可以是文本格式,图像格式和其他恰当的类型。

在发送响应体之前,s e r v l e t应尽量捕获和处理任何类型的错误。你可能还记得(因为我们反

复提到) ,H T T P规定状态码和H T T P头必须在响应体之前发送。一旦发出响应信息,哪怕只发出

一个字符,都来不及更改状态码和 H T T P头信息。为避免这种“太迟”的尴尬局面,可以使用

B y t e A r r a y O u t p u t S t r e a m缓存或H T M L生成器包。

(2) 日志

s e r v l e t能将它们的行为和错误通过使用l o g()方法写入日志文件中:

public void ServletContext.log(String msg)

public void ServletContext.log(Exception e, String msg)

单个参数的方法是将给定的消息写入 s e r v l e t日志中,这个日志通常是事件日志文件。带两个

参数的版本是将给定的消息和异常堆栈信息写入 s e r v l e t日志中。这两种方法的输出格式和日志文

第二章 预 备 知 识计计51 下载件的路径都是服务器相关的。

G e n e r i c S e r v l e t类也提供了l o g()方法:

public void GenericServlet.log(String msg)

这是S e r v l e t C o n t e x t方法的另一个版本,移入G e n e r i c S e r v l e t类中是为了使用方便,这个方法

允许s e r v l e t像下面这样简单调用,写入s e r v l e t日志之中:

log(msg);

然而,G e n e r i c S e r v l e t没提供两个参数的l o g()版本,这可能是个疏忽,会在将来的版本中

增加。现在,s e r v l e t可以通过调用下列函数执行类似的功能:

getServletContext().log(e , msg);

l o g ( )方法提供了跟踪s e r v l e t行为的途径,可用于帮助程序调试。它也提供了保存 s e r v l e t遇到

的任何错的误详尽描述方法,这个描述可以与发送给客户端的一样,也可以更为详尽。

(3) 报告错误

除了能为服务器管理员记录错误和异常外,开发过程中显示问题的详细描述也是很方便的。

遗憾的是,异常不能以S t r i n g形式返回它的堆栈跟踪信息,它只能显示堆栈跟踪信息到P r i n t S t r e a m

或P r i n t Wr i t e r中。要以S t r i n g类型收集堆栈跟踪信息,就必须绕些弯子。必须让异常显示到特殊的

P r i n t Wr i t e r中,这个P r i n t Wr i t e r是建立在B y t e A r r a y O u t p u t S t r e a m基础上的,它能捕获输出并转化为

S t r i n g类型。c o m . o r e i l l y. s e r v l e t . S e r v l e t U t i l s类有个g e t S t a c k Tr a c e S t r i n g ( )方法可完成这种工作:

public static String getStackTraceAsString(Exception  e)  {

ByteArrayOutputStream bytes = new ByteArrayOutputStream();

PrintWriter writer = new PrintWriter(bytes, true);

e.printStackTrace(writer);

return bytes.toString();

}

下面是提供包括I O E x c e p t i o n堆栈跟踪信息的Vi e w F i l e :

//Return the file

try {

ServletUtils.returnFile(file ,out );

}

catch (FileNotFoundException e) {

log("Could not find file: " + e.getMessage());

res.sendError(res.SC_NOT_FOUND);

}

catch (IOException e) {

getServletContext().log(e, "Problem sending file");

res.sendError(res.SC_INTERNAL_SERVER_ERROR,

ServletUtils.getStackTraceAsString(e));

}

(4) 异常处理

前面曾经提到,抛出的异常如果没被 s e r v l e t捕获,就将被服务器捕获。服务器如何处理异常

是因服务器而异的:它也许给客户端发送消息和堆栈跟踪信息;它或许自动地记录异常;它甚

52计计第一部分 JSP    入 门

下载至也许在s e r v l e t上调用d e s t r o y()方法并重新下载。

为某特定的服务器设计和开发的 s e r v l e t可以优化服务器的行为,而设计为跨多种服务器的

s e r v l e t做不到,如果这种s e r v l e t需要特殊的异常处理,就得考虑相应的服务器因素。

有些异常类型是 s e r v l e t必须捕捉的, s e r v l e t仅能将I O E x c e p t i o n、S e r v l e t E x c e p t i o n或

R u n t i m e E x c e p t i o n的子类异常传给它的服务器。原因与方法的特性有关, s e r v l e t的s e r v i c e()方

法在它的t h r o w s语句中声明抛出I O E x c e p t i o n和S e r v l e t E x c e p t i o n异常,因此它不捕获编译过程中

的异常。R u n t i m e E x c e p t i o n是个特殊类型的异常,它不需要在 t h r o w s语句中被声明,一个常见的

例子是N u l l P o i n t E x c e p t i o n。

i n i t()方法和d e s t r o y()方法也有自己的特征,i n i t方法声明仅抛出S e r v l e t E x c e p t i o n异常,

而d e s t r o y()方法声明不抛出异常。

S e r v l e t E x c e p t i o n是j a v a . l a n g . E x c e p t i o n的子类,此类是s e r v l e t相关的,并在j a v a x . s e r v l e t . p a c k a g e

包中被定义。这个异常表明常规s e r v l e t问题,它与j a v a . l a n g . E x c e p t i o n具有相同的构造函数:一个

不带参数,一个仅带一个消息字串参数。捕捉这种异常的服务器可能用任何合适的方法处理它。

j a v a x . s e r v l e t包定义了一个S e r v l e t E x c e p t i o n子类U n a v a i l a b l e E x c e p t i o n,这个异常表明s e r v l e t

不可访问,或是临时的,或是永久的。

U n a v a i l a b l e E x c e p t i o n有两个构造函数:

java.servlet.UnavailableException(Servlet servlet , String  msg)

java.servlet.UnavailableException(int Seconds , Servlet servlet , String  msg)

双参数的构造函数创建一个新的异常,表明指定的 s e r v l e t永久性地不可访问,并且由m s g给

出解释;三参数的构造函数创建一个新的异常,表明指定的 s e r v l e t暂时不可访问,并由m s g给出

解释,不可访问的时间长度由s e c o n d s给出,这仅仅是个估计时间。

2.4   SQL语言

J S P的数据库方面所依赖的是J D B C,而J D B C的强大在于:J D B C可以使J a v a成为一种能同不

均匀的数据库环境打交道的强大工具,这种不均匀的数据库环境尽管的确差别很大,但是无论

是哪一种关系数据库,从O r a c l e到D B 2到S y b a s e再到MS SQL Server,有一点是相同的,那就是

S Q L语言-结构化查询语言。

尽管各个不同的数据库厂商对 S Q L做了各自的扩展,如:O r a c l e的P L - S Q L、Microsoft SQL

S e r v e r的Tr a n s a c t - S Q L、还有S Q L语言鼻祖I B M的DB2 SQL,每一个R D B M S厂商都宣称自己的

扩展是最优秀的,然而,这些不同的S Q L仍然有共同点,他们都基于ANSI SQL 92。

S Q L不是一门特别复杂的语言,不过如果想要学好 S Q L,特别是各个不同厂商特有的 S Q L,

仍然需要特别的努力,仅仅讲述 S Q L中最基本的语句,本书在第一部分的例子程序中也只会用

到最基本的S Q L语句,在第二部分的例子中由于将会使用存储过程,所以会使用一些扩展的 S Q L

语言,这些扩展将在需要时再进行讲解。

2.4.1   SQL子类型

S Q L语言的子类型包括:数据处理语言( D M L) 、数据定义语言( D D L)和数据控制语言

第二章 预 备 知 识计计53 下载(D C L) 。

数据处理语言D M L完成在数据库中确定、修改、添加、删除某一数据的值的任务,下面是

在J a v a和J D B C中常用到的一些数据处理S Q L语句:

SELECT   在数据库中依据某一种规则查询数据。

I N S E RT   向数据库中添加一行数据。

DELETE   删除数据库中的某一行数据。

U P D ATE   改变数据库中已有记录。

数据定义语言D D L完成定义数据库的结构,包括数据库本身、数据表、目录、视图等数据

库元素:

C R E ATE   在数据库中建立一个元素。

DROP   在数据库中删除一个元素。

数据控制语言D C L完成管理数据库中数据的存储权限的任务,下面是在 J a v a和J D B C中常用

到的一些数据控制S Q L语句:

GRANT   设置某一用户或用户组可以某种形式访问数据库中的某一元素。

REVOKE   去掉某一用户或用户组可以某种形式访问数据库中的某一元素的权利。

2.4.2   SQL语言的具体命令和使用

下面的范例使用了 Microsoft  SQL Server 7.0自带的样本数据库,需要说明的是,尽管

Microsoft SQL Server在J a v a的支持上比其他的R D B M S如O r a c l e、D B 2要差,但Microsoft SQL

S e r v e r比较容易使用,本书在第一部分的例子基本上都是使用 Microsoft SQL Server 7.0建立的;

另一方面,第一部分的例子没有使用到SQL Server特有的S Q L语句的情况,所以读者可把这些例

子移植到O r a c l e、D B 2或是其他任何支持S Q L的数据库系统上。

1. SELECT 语句

SELECT 无疑是S Q L语句中最常用的语句,一个S E L E C T语句可以十分简单,也可以十分复

杂,下面先从最简单的开始:

在Query Analyzer中选择数据库为N o r t h w i n d,然后输入:

SELECT * FROM customers

执行它,则可以看见如图2 - 1所示的结果:

这条S E L E C T语句的含义从字面上就很好理解,即:从 c u s t o m e r s数据表中检索出全部数据,

“*”表示全部的列。

如果把“*”换为“C u s t o m e r I D” ,则结果将会变为:

customerID 

---------- 

ALFKI

ANATR

ANTON

. . . . . .

注意:为了节省篇幅,下面的例子执行结果将不再使用图形表示,读者应当可以看懂。

54计计第一部分 JSP    入 门

下载图2 - 1

“*”中的内容也可以包含多项,其中用“,”隔开即可,如:

"SELECT CustomerID,CompanyName from customers"。

(1) 使用别名

数据表中某一列的名称应该是有意义的,但不幸的是,这仅仅是对某一些人而言,常常有

这种情况:某一位数据库建立者创建的数据库中包含的列名对他自己来说是有明确意义的,但

对另外一些人来说却是不知所云。

解决办法就是在查询的时候为数据表的某一列建立一个别名,下面举例说明:

select phone as "电话", fax as "传真"from customers

结果如下:

电话 传真

-----------------------    ----------------------- 

0 3 0 - 0 0 7 4 3 2 1 0 3 0 - 0 0 7 6 5 4 5

(5) 555-4729  (5) 555-3745

(5) 555-3932 N U L L

. . . . . .

这样,原先对于不懂英文的人来说不知所云的“ p h o n e”和“f a x”现在变成了简单易懂的

第二章 预 备 知 识计计55 下载“电话”和“传真” 。

(2) 在查询输出中加入文本

尽管上面加上别名之后的输出结果让人容易理解,但仍然不是太明确,在查询输出中加入

文本的方法将可以输出完整的句子。

select CompanyName as "公司名称","公司的电话是",phone as"电话"from customers

结果是:

公司名称 电话

-----------------------------------------------   -------------------------    ------------------------ 

Alfreds Futterkiste 公司的电话是 0 3 0 - 0 0 7 4 3 2 1

Ana Trujillo Emparedados y helados 公司的电话是 (5) 555-4729

Antonio Moreno Ta q u e ría 公司的电话是 (5) 555-3932

. . . . . . . . . . . .

在J D B C和J S P中,在查询中加入文本有助于直接输出可以用在网页中且包含 H T M L代码的查

询结果。

( 3 ) ORDER BY 子句

ORDER BY子句的作用是将输出结果按照某一列按升序或降序排列,其中,升序排列的附

加命令是A S C,而降序排列的附加命令是D E S C,缺省为升序排列。

SELECT CompanyName,phone from customers ORDER BY phone

结果如下:

C o m p a n y N a m e p h o n e

--------------------------                    ------------------------ 

Maison Dewey (02) 201 24 67

S u p rêmes délices  (071) 23 67 22 20

Rancho grande  (1) 123-5555

而如果是:

SELECT CompanyName,phone from customers ORDER BY phone DESC

则结果是:

C o m p a n y N a m e p h o n e

-----------------------------           ------------------------ 

Wartian Herkku 9 8 1 - 4 4 3 6 5 5

Bon app' 9 1 . 2 4 . 4 5 . 4 0

Wilman Kala 90-224 8858

(4) WHERE短语

W H E R E是一个有条件的选择数据的短语,它指定只返回那些和 W H E R E短语重指定的条件

一致的数据。

W H E R E短语的条件可以包含关系运算、布尔运算、 L I K E、I N、B E T W E E N等等,甚至可以

包含其他的S E L E C T语句的查询结果。下面分别介绍:

56计计第一部分 JSP    入 门

下载1) 关系运算。S Q L语言的关系运算包括: “=” 、 “>” 、 “<” 、 “> =” 、 “< =” 、 “< >” 。从这些符

号本身就应该可以理解其意义,下面是一个实例:

SELECT CompanyName,City from customers where City="London"

目的是找出客户中所有所在地为伦敦的公司。结果如下

C o m p a n y N a m e City 

------------------------                    --------------- 

Around the Horn L o n d o n

B's Beverages  L o n d o n

Consolidated Holdings  L o n d o n

Eastern Connection L o n d o n

N o r t h / S o u t h L o n d o n

Seven Seas Imports L o n d o n

2)  布尔运算。S Q L语言的布尔运算包括“ A N D” 、 “O R” 、 “N O T” ,即“与” 、 “或” 、 “非”

三种运算。

例子如下,目的是找出订单中所有和代号“V I N E T”的公司相关并且由2号雇员处理的订单。

SELECT OrderID,CustomerID,EmployeeID FROM orders 

WHERE CustomerID="VINET"AND EmployeeID=2

结果如下:

O r d e r I D C u s t o m e r I D EmployeeID  

-----------                 --------------------               -------------------- 

1 0 2 9 5 V I N E T 2

1 0 7 3 7 V I N E T 2

(5) LIKE运算

L I K E运算的用途是在那些文本类型的数据中找出某一特定的字符串,加上通配符的使用,

只需学会使用L I K E运算就可以构造一个简单的搜索引擎了。

在L I K E运算中包含如下两个通配符:

% 代表多个字符

_ 代表一个字符

例子如下:

第一个例子在客户数据表中查找所有名称中含有“ H u n g r y”的公司:

SELECT CompanyName,CustomerID FROM customers 

WHERE CompanyName LIKE "%Hungry%"

结果如下:

C o m p a n y N a m e CustomerID 

----------------------------------------          -------------------- 

Hungry Coyote Import Store H U N G C

Hungry Owl All-Night Grocers H U N G O

第二章 预 备 知 识计计57 下载第二个例子是在订单数据表中查询所有的订单号以“ 1 0 2 4”开头的,且一共为五位的订单。

SELECT OrderID,CustomerID FROM orders WHERE OrderID LIKE "1024_"

结果如下:

O r d e r I D CustomerID 

-----------                -------------------- 

1 0 2 4 9 TO M S P

1 0 2 4 8 V I N E T

(6) IN运算

I N运算通过一个预先定义好的值表来限定所用值的范围,当所给参数和表中的值匹配时才

认为是“真” 。

例如,在订单数据表中查询所有代号为V I N E T和TO M S P的客户:

SELECT CustomerID,OrderID,ShipName FROM orders 

WHERE CustomerID IN("VINET","TOMSP")

结果如下:

C u s t o m e r I D O r d e r I D S h i p N a m e

-----------------     ----------------      ---------------------------- 

TO M S P 1 0 2 4 9 Toms Spezialit?ten

TO M S P 1 0 4 3 8 Toms Spezialit?ten

TO M S P 1 0 4 4 6 Toms Spezialit?ten

TO M S P 1 0 5 4 8 Toms Spezialit?ten

TO M S P 1 0 6 0 8 Toms Spezialit?ten

TO M S P 1 0 9 6 7 Toms Spezialit?ten

V I N E T 1 0 2 4 8 Vins et alcools Chevalier

V I N E T 1 0 2 7 4 Vins et alcools Chevalier

V I N E T 1 0 2 9 5 Vins et alcools Chevalier

V I N E T 1 0 7 3 7 Vins et alcools Chevalier

V I N E T 1 0 7 3 9 Vins et alcools Chevalier

(7) BETWEEN运算

和I N运算一样,B E T W E E N运算也是限定所用值的范围,当所给参数和预设的值匹配时才认

为是“真” 。不过B E T W E E N运算所限定的方式不是给出一个值表,而是给出一个最大值和最小

值。当数据表中的值在这个最大和最小值之间(包括最大值和最小值)时认为是“真” 。

例如:

要找出订单数据表中所有订单号在1 0 2 4 9和1 0 2 5 4之间的订单:

SELECT CustomerID,OrderID,ShipName FROM orders 

WHERE OrderID BETWEEN 10249 AND 10254

结果如下:

58计计第一部分 JSP    入 门

下载C u s t o m e r I D O r d e r I D ShipName  

-----------------         ----------------        ----------------------------- 

TO M S P 1 0 2 4 9 Toms Spezialit?ten

H A N A R 1 0 2 5 0 Hanari Carnes

V I C T E 1 0 2 5 1 Victuailles en stock

S U P R D 1 0 2 5 2 S u p rêmes délices

H A N A R 1 0 2 5 3 Hanari Carnes

C H O P S 1 0 2 5 4 Chop-suey Chinese

也许有的读者需要得到在最大值和最小值之间,但并不包括最大值和最小值的数据,那么

可以这样做:

SELECT CustomerID,OrderID,ShipName FROM orders

WHERE OrderID BETWEEN 10249 AND 10254

AND NOT OrderID IN (10249,10254)

这样结果就变成了:

C u s t o m e r I D O r d e r I D S h i p N a m e

------------------     -----------------           ---------------------------

H A N A R 1 0 2 5 0 Hanari Carnes

V I C T E 1 0 2 5 1 Victuailles en stock

S U P R D 1 0 2 5 2 Suprêmes délices

H A N A R 1 0 2 5 3 Hanari Carnes

(8) 使用函数

尽管大部分关系数据库系统( R D B M S)都扩充了可以在S Q L中使用的函数,许多数据库系

统还允许用户自己扩充函数,但下面的几个函数总是可以使用的:

AVG   返回某一组中的值除以该组中值的个数的和 。

COUNT   返回一组行或值中行或值的个数 。

MAX   返回一组值中的最大值 。

MIN   返回一组值中的最小值。

下面是实际的例子:

求出所有订单的数量总和:

SELECT COUNT(Freight) FROM orders

求出所有订单的运费平均值:

SELECT AVG(Freight) FROM orders

求出所有订单的运费最大值

SELECT MAX(Freight) FROM orders

求出所有订单的运费最小值

SELECT MIN(Freight) FROM orders

(9) 子查询

第二章 预 备 知 识计计59 下载子查询的概念在于将一个查询的结果作为另一个查询的条件,举例如下:

在订单数据表中的客户公司是使用公司代号来表示的,如果需要查询运费在 5 0 0以上的公司

的名称和电话,就需要使用子查询这个概念:

SELECT CompanyName,phone FROM customers

WHERE CustomerID IN (

SELECT CustomerID from orders

WHERE freight>500

)

结果得到了需要的数据:

C o m p a n y N a m e p h o n e

---------------------                              --------------------------

Ernst Handel 7 6 7 5 - 3 4 2 5

Great Lakes Food Market (503) 555-7555

Hungry Owl All-Night Grocers 2967 542

Queen Cozinha ( 11) 555-11 8 9

Q U I C K - S t o p 0 3 7 2 - 0 3 5 1 8 8

Rattlesnake Canyon Grocery (505) 555-5939

Save-a-lot Markets (208) 555-8097

White Clover Markets (206) 555-411 2

2. 使用数据修改命令

S Q L语言中数据的修改命令包括:

I N S E RT 建立记录。

D E L E T E 删除记录。

U P D AT E 修改记录。

(1) INSERT语句

I N S E RT语句在使用时有两种不同的格式。需要注意的是, I N S E RT语句假定需要插入数据

的数据表已经用C R E AT E语句或其他工具建立。

第一种用法是不列出数据表的各个列名,而按照数据表建立时的顺序将数据列出:

INSERT INTO Customers VALUES

("AAAAA","AAAAA Co.Ltd.","riso liao","Owner","Peking University","Bei Jing","Bei

Jing","HaiDian","100871","86-10-62754178","86-10-62763126")

第二种用法是在数据表的后面按照后面数据需要插入的列的顺序列出数据表中各个列的名称:

INSERT INTO Customers(

CustomerID,CompanyName,ContactName,ContactTitle,Address,City,Region,PostalCode,

Country,Phone,Fax

)

VALUES

("AAAAA","AAAAA Co.Ltd.","riso liao","Owner","Peking University","Bei Jing","Bei

Jing","HaiDian","100871","86-10-62754178","86-10-62763126")

60计计第一部分 JSP    入 门

下载上面两条语句的作用都一样,不过在实际使用中建议使用第二种方法,因为第二种方法可

以让数据和数据要插入的列一一对应,而且还有利于插入空值,例如:现在需要加入到记录中

的这个公司数据相当不完整,只有公司名称和代号,当采用第一种方法时,需要这样书写 S Q L

语句:

INSERT INTO Customers VALUES

("AAAAA","AAAAA Co.Ltd.",NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)

而第二种方法只需要书写如下语句即可:

INSERT INTO Customers(

CustomerID,CompanyName)

VALUES

("AAAAA","AAAAA Co.Ltd.")

(2) DELETE语句

D E L E T E语句的使用是相当简单的,具体就是:

DELETE FROM 表名 条件

其中条件不是必需的,当没有条件时,就意味着删除表中的所有记录。

例如,语句DELETE FROM customers WHERE CustomerID=”A A A A A”将删除刚才建立的

记录;而语句 DELETE FROM customers将删除c u s t o m e r s数据表中的所有记录。

(3) UPDAT E语句

U P D AT E语句的作用是将数据库中某一条记录的某一个记录域更新,语句格式如下:

UPDATE 数据表 SET 列名=新数据 条件

和D E L E T E语句一样,这里的条件也可以是没有的,如果没有条件,那么数据表中的每一条

记录都将被更新。

现在来试验一下U P D AT E语句:

首先看一看数据本来的样子:

SELECT CustomerID,CompanyName FROM customers

可以看到第一行记录为:

C u s t o m e r I D C o m p a n y N a m e

------------------       -------------------------- 

A L F K I Alfreds Futterkiste

现在执行一个U P D AT E:

UPDATE customers SET CompanyName=’AAAAA’

WHERE CustomerID=’ALFKI’

再执行一下SELECT CustomerID,CompanyName FROM customers

发现结果变成了:

C u s t o m e r I D CompanyName 

----------------         ------------------------

A L F K I A A A A A

第二章 预 备 知 识计计61 下载这样,数据库中的一条语句就被更新了。

2.5   JDBC

本节将在上节讲述的S Q L语言的基础上介绍J D B C,J D B C使得在J a v a程序中可以轻松地操纵

数据库:从企业级的 O r a c l e、S y b a s e、D B 2到最简单的A c c e s s、M y S Q L。在J S P中,就是利用

J D B C来访问数据库的。

2.5.1   什么是 J D B C

JDBC 是一种用于执行 SQL 语句的 Java API,它由一组用 Java 编程语言编写的类和接口组

成。JDBC  为工具/数据库开发人员提供了一个标准的 A P I,使他们能够用纯 Java API 来编写数

据库应用程序。

有了 J D B C,向各种关系数据库发送 SQL  语句就是一件很容易的事。换言之,有了 J D B C

A P I,就不必为访问 Sybase 数据库专门写一个程序,为访问 Oracle 数据库又专门写一个程序,

为访问 Informix 数据库又写另一个程序,等等。只需用 JDBC API 写一个程序就够了,它可向

相应的数据库发送 SQL 语句。而且,使用 Java 编程语言编写的应用程序,无须去忧虑要为不同

的平台编写不同的应用程序。将 Java 和 JDBC  结合起来将使程序员只需写一遍程序就可让它在

任何平台上运行。

Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库

应用程序的杰出语言。所需要的只是 Java 应用程序与各种不同数据库之间进行对话的方法。而

JDBC 正是作为此种用途的机制。

JDBC 扩展了 Java 的功能。例如,用 Java 和 JDBC API 可以发布含有 applet 的网页,而该

applet 使用的信息可能来自远程数据库。企业也可以用 JDBC 通过 Intranet 将所有职员连到一个

或多个内部数据库中(即使这些职员所用的计算机有 Wi n d o w s、 Macintosh 和 UNIX 等各种不

同的操作系统) 。随着越来越多的程序员开始使用 Java 编程语言,对从 Java 中便捷地访问数据

库的要求也在日益增加。

MIS 管理员们都喜欢 Java 和 JDBC 的结合,因为它使信息传播变得容易和经济。企业可继

续使用它们安装好的数据库,并能便捷地存取信息,即使这些信息是存储在不同数据库管理系

统上。新程序的开发期很短。安装和版本控制将大为简化。程序员可只编写一遍应用程序或只

更新一次,然后将它放到服务器上,随后任何人就都可得到最新版本的应用程序。对于商务上

的销售信息服务, Java 和 JDBC 可为外部客户提供获取信息的更新更好方法。

1. JDBC 的用途

简单地说,JDBC 可做三件事:

• 与数据库建立连接。

• 发送 SQL 语句。

• 处理结果。

下列代码段给出了以上三步的基本示例:

Connection con = DriverManager.getConnection ("jdbc:odbc:wombat", "login",

62计计第一部分 JSP    入 门

下载"password");

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");

while (rs.next()) {

int x = rs.getInt("a");

String s = rs.getString("b");

float f = rs.getFloat("c");

}

2. JDBC 是一种低级 API ,是高级 API 的基础

JDBC 是个”低级”接口,就是说,它用于直接调用 SQL 命令。在这方面它的功能极佳,

并比其他的数据库连接 A P I更易于使用,但它同时也被设计为一种基础接口,在它之上可以建

立高级接口和工具。高级接口是”对用户友好的”接口,它使用的是一种更易理解和更为方便

的 A P I,这种 API  在幕后被转换为诸如 JDBC  这样的低级接口。在编写本文时,正在开发两种

基于 JDBC 的高级 A P I:

一种是用于 Java 的嵌入式 S Q L。至少已经有一个提供者计划编写它。DBMS 实现 S Q L:一

种专门设计来与数据库联合使用的语言。JDBC 要求 SQL 语句必须作为 String 传给 Java 方法。

相反,嵌入式 SQL 预处理器允许程序员将 SQL 语句直接与 Java 混在一起使用。例如,可在

SQL 语句中使用 Java 变量,用以接受或提供 SQL 值。然后,嵌入式 SQL 预处理器将通过

JDBC 调用把这种 Java/SQL 的混合物转换为 J a v a。

另一种关系数据库表到 Java 类的直接映射。JavaSoft 和其他提供者都声称要实现该 A P I。

在这种”对象/关系”映射中,表中的每行对应于类的一个实例,而每列的值对应于该实例的一

个属性。于是,程序员可直接对 Java 对象进行操作;存取数据所需的 SQL  调用将在”掩盖下”

自动生成。此外还可提供更复杂的映射,例如将多个表中的行结合进一个 Java 类中。

随着人们对 JDBC 的兴趣日益浓厚,越来越多的开发人员一直在使用基于 JDBC 的工具,

以使程序的编写更加容易。程序员也一直在编写力图使最终用户对数据库的访问变得更为简单

的应用程序。例如,应用程序可提供一个选择数据库任务的菜单。任务被选定后,应用程序将

给出提示及空白供填写执行选定任务所需的信息。所需信息输入后,应用程序将自动调用所需

的 SQL 命令。在这样一种程序的协助下,即使用户根本不懂 SQL  的语法,也可以执行数据库

任务。

3. JDBC 与 ODBC 和其他 API 的比较

目前,O D B C(开放式数据库连接)API 可能是使用最广的、用于访问关系数

据库的编程接口。它能在几乎所有平台上连接几乎所有的数据库。为什么 Java 不使用

O D B C?

对这个问题的回答是:Java 可以使用 O D B C,但最好是在 JDBC  的帮助下以 J D B C - O D B C

桥的形式使用,这一点稍后再讲解。现在的问题已变成: ”为什么需要 J D B C”? 回答如下:

ODBC 不适合直接在 Java 中使用,因为它使用 C 语言接口。从 Java 调用本地 C 代码在安

全性、实现性、坚固性和程序的自动移植性方面都有许多缺点。

从 ODBC C API 到 Java API 的字面翻译是不可取的。例如,Java 没有指针,而 ODBC 却对

指针用得很广泛(包括很容易出错的指针 “void *” ) 。你可以将 JDBC 想像成被转换为面向对

第二章 预 备 知 识计计63 下载象接口的 O D B C,而面向对象的接口对 Java 程序员来说较易于接收。

ODBC 很难学。它把简单和高级功能混在一起,而且即使对于简单的查询,其选项也极为

复杂。相反,JDBC 尽量保证简单功能的简便性,而同时在必要时允许使用高级功能。 启用”

纯 Java “机制需要像 JDBC 这样的 Java API。如果使用 O D B C,就必须手工地将 ODBC 驱动程

序管理器和驱动程序安装在每台客户机上。如果完全用 Java 编写 JDBC 驱动程序,则 JDBC 代

码在所有 Java 平台上(从网络计算机到大型机)都可以自动安装、移植并保证安全性。

总之,JDBC API 对于基本的 SQL 抽象和概念是一种自然的 Java 接口。它建立在ODBC 上,

而不是从零开始。因此,熟悉 ODBC  的程序员将发现 JDBC 很容易使用。JDBC 保留了 O D B C

的基本设计特征;事实上,两种接口都基于 X/Open SQL CLI(调用级接口) 。它们之间最大的

区别在于:JDBC 以 Java 风格与优点为基础并进行优化,因此更加易于使用。

最近,Microsoft 又引进了 ODBC 之外的新 A P I: R D O、 ADO 和 OLE DB。这些设计在许

多方面与 JDBC 是相同的,即它们都是面向对象的数据库接口且基于可在 ODBC 上实现的类。

但在这些接口中,未看见有特别的功能使我们要转而选择它们来替代 O D B C,尤其是在 O D B C

驱动程序已建立起较为完善的市场的情况下。它们最多也就是在 ODBC 上加了一种装饰而已。

这并不是说 JDBC 不需要从其最初的版本再发展了;然而,我们觉得大部份的新功能应归入诸

如前一节中所述的对象/关系映射和嵌入式 SQL 这样的高级 A P I。

4. 两层模型和三层模型

JDBC API 既支持数据库访问的两层模型,同时也支持三层模型。

在两层模型中,Java applet 或应用程序将直接与数据库进行对话。这将需要一个 JDBC 驱动

程序来与所访问的特定数据库管理系统进行通信。用户的 SQL 语句被送往数据库中,而其结果

将被送回给用户。数据库可以位于另一台计算机上,用户通过网络连接到上面。这就叫做客户

机/服务器配置,其中用户的计算机为客户机,提供数据库的计算机为服务器。网络可以是

I n t r a n e t(它可将公司职员连接起来) ,也可以是 I n t e r n e t。

在三层模型中,命令先是被发送到服务的”中间层“,然后由它将 SQL 语句发送给数据库。

数据库对 SQL 语句进行处理并将结果送回到中间层,中间层再将结果送回给用户。 MIS 管理员

们都发现三层模型很吸引人,因为可用中间层来控制对公司数据的访问和可作的更新的种类。

中间层的另一个好处是,用户可以利用易于使用的高级 A P I,而中间层将把它转换为相应的低

级调用。而且,许多情况下三层结构可提供一些性能上的好处。

到目前为止,中间层通常都用 C 或 C++ 这类语言来编写,这些语言执行速度较快。然而,

随着最优化编译器(它把 Java 字节代码转换为高效的特定于机器的代码)的引入,用 Java 来实

现中间层将变得越来越实际。这将是一个很大的进步,它使人们可以充分利用 Java 的诸多优点

(如坚固、多线程和安全等特征) 。JDBC 对于从 Java 的中间层来访问数据库非常重要。

5. SQL 的一致性

结构化查询语言 (SQL) 是访问关系数据库的标准语言。其困难之处在于:虽然大多数的

D B M S(数据库管理系统)对其基本功能都使用了标准形式的 S Q L,但它们却不符合最近为更

高级的功能定义的标准 SQL 语法或语义。例如,并非所有的数据库都支持存储程序或外部连接,

那些支持这一功能的数据库又相互不一致。人们希望 SQL 中真正标准的那部份能够进行扩展以

64计计第一部分 JSP    入 门

下载包括越来越多的功能。但同时 JDBC API 又必须支持现有的 S Q L。

JDBC API 解决这个问题的一种方法是允许将任何查询字符串一直传到所涉及的 DBMS 驱

动程序上。这意味着应用程序可以使用任意多的 SQL  功能,但它必须冒这样的风险:有可能在

某些 DBMS 上出错。事实上,应用程序查询甚至不一定是 S Q L,或者说它可以是为特定的

DBMS 设计的 SQL 的专用派生物(例如,文档或图像查询) 。

JDBC 处理 SQL 一致性问题的第二种方法是提供 ODBC 风格的转义子句。

转义语法为几个常见的 SQL 分歧提供了一种标准的 JDBC 语法。例如,对日期文字和已存

储过程的调用都有转义语法。

对于复杂的应用程序, JDBC 用第三种方法来处理 SQL 的一致性问题。它利用 D a t a b a s e

MetaData 接口来提供关于 DBMS 的描述性信息,从而使应用程序能适应每个 DBMS  的要求和

功能。

由于 JDBC API 将用做开发高级数据库访问工具和 API  的基础,因此它还必须注意其所有

上层建筑的一致性。 “符合 JDBC 标准” 代表用户可依赖的 JDBC 功能的标准级别。要使用这一

说明,驱动程序至少必须支持 ANSI SQL-2 Entry Level(ANSI SQL-2 代表美国国家标准局 1 9 9 2

年所采用的标准。Entry Level 代表 SQL  功能的特定清单) 。驱动程序开发人员可用 JDBC API

所带的测试工具包来确定他们的驱动程序是否符合这些标准。

“符合 JDBC 标准” 表示提供者的 JDBC 实现已经通过了 JavaSoft 提供的一致性测试。这些

一致性测试将检查 JDBC API 中定义的所有类和方法是否都存在,并尽可能地检查程序是否具

有 SQL Entry Level 功能。当然,这些测试并不完全,而且 JavaSoft 目前也无意对各提供者的实

现进行标级。但这种一致性定义的确可对 JDBC 实现提供一定的可信度。随着越来越多的数据

库提供者、连接提供者、Internet 提供者和应用程序编程员对 JDBC API 的接受,JDBC  也正迅

速成为 Java 数据库访问的标准。

2.5.2   JDBC 产品

在编写本文时,已经有许多可用的 J D B C产品问世。当然,本节中的信息将很快成为过时信

息。因此,有关最新的信息,请查阅 JDBC 的网站,可通过从以下 URL 开始浏览找到:

h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j d b c

1. JavaSoft 框架

JavaSoft 提供三种 JDBC 产品组件,它们是 Java 开发工具包 (JDK) 的组成部分:

JDBC 驱动程序管理器、JDBC 驱动程序测试工具包和 JDBC-ODBC 桥。

JDBC 驱动程序管理器是 JDBC 体系结构的支柱。它实际上很小,也很简单;其主要作用是

把 Java 应用程序连接到正确的 JDBC 驱动程序上,然后退出。

JDBC 驱动程序测试工具包为使 JDBC 驱动程序运行你的程序提供一定的可信度。只有通过

JDBC 驱动程序测试包的驱动程序才被认为是符合 JDBC 标准TM 的。

JDBC-ODBC 桥使 ODBC 驱动程序可被用作 JDBC 驱动程序。它的实现为 JDBC 的快速发

展提供了一条途径,其长远目标是提供一种访问某些不常见的 D B M S(如果对这些不常见的

DBMS 未实现 J D B C)的方法。

第二章 预 备 知 识计计65 下载2. JDBC 驱动程序的类型

目前所知的 JDBC 驱动程序可分为以下四个种类:

1) JDBC-ODBC 桥加 ODBC 驱动程序:JavaSoft 桥产品利用 ODBC 驱动程序提供 JDBC 访

问。注意,必须将 ODBC  二进制代码(许多情况下还包括数据库客户机代码)加载到使用该驱

动程序的每个客户机上。因此,这种类型的驱动程序最适合于企业网(这种网络上客户机的安

装不是主要问题) ,或者是用 Java 编写的三层结构的应用程序服务器代码。

2) 本地 API - 部分用 Java 来编写的驱动程序: 这种类型的驱动程序把客户机 API 上的

JDBC 调用转换为 O r a c l e、 S y b a s e、I n f o r m i x、DB2 或其他 DBMS 的调用。注意,像桥驱动程

序一样,这种类型的驱动程序要求将某些二进制代码加载到每台客户机上。

3) JDBC 网络纯 Java 驱动程序:这种驱动程序将 JDBC 转换为与 DBMS 无关的网络协议,

之后这种协议又被某个服务器转换为一种 DBMS 协议。这种网络服务器中间件能够将它的纯

Java 客户机连接到多种不同的数据库上。所用的具体协议取决于提供者。通常,这是最为灵活

的 JDBC 驱动程序。有可能所有这种解决方案的提供者都提供适合于 Intranet 用的产品。为了使

这些产品也支持 Internet 访问,它们必须处理 Web 所提出的安全性、通过防火墙的访问等方面

的额外要求。几家提供者正将 JDBC 驱动程序加到他们现有的数据库中间件产品中。

4) 本地协议纯 Java 驱动程序:这种类型的驱动程序将 JDBC 调用直接转换为 DBMS 所使用

的网络协议。这将允许从客户机机器上直接调用 DBMS  服务器,是 Intranet 访问的一个很实用

的解决方法。由于许多这样的协议都是专用的,因此数据库提供者自己将是主要来源,有几家

提供者已经开发出了这样的驱动程序。

最后,我们预计第 3、4 类驱动程序将成为从 JDBC  访问数据库的首选方法。第 1、2 类驱

动程序在直接的纯 Java 驱动程序还没有上市前将会作为过渡方案来使用。对第 1、2 类驱动程

序可能会有一些变种(下表中未列出) ,这些变种要求有连接器,但通常这些是更加不可取的解

决方案。第 3、4 类驱动程序提供了 Java 的所有优点,包括自动安装(例如,通过使用 J D B C

驱动程序的 applet applet来下载该驱动程序) 。

表2 - 3显示了这 4 种类型的驱动程序及其属性:

表2 - 3

驱动程序种类 纯 J a v a 网络协议

JDBC-OCBC 桥 非 直接

基于本地 API 的 非 直接

JDBC 网络的 是 要求连接器

基于本地协议的 是 直接

3. JDBC 驱动程序的获取

在编写本文时,已经有许多J D B C驱动程序存在,如果使用的是We b l o g i c或是We b s p h e r e,产

品本身就带有不少数据库系统的驱动程序。许多重要的商业数据库系统也自带了适合自己的

J D B C驱动程序,如:O r a c l e、S y b a s e、IBM DB2。要获取关于驱动程序的最新信息,请查阅

JDBC 的网站,其网址为: http:// java.sun.com/products/jdbc。

66计计第一部分 JSP    入 门

下载2.5.3   连接概述

Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL 语句和在该连接上所返

回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。

1. 打开连接

与数据库建立连接的标准方法是调用 D r i v e r M a n a g e r.getConnection ()方法。该方法接受含有

某个 URL 的字符串。DriverManager 类(即所谓的 JDBC 管理层)将尝试找到可与那个URL 所

代表的数据库进行连接的驱动程序。DriverManager 类存有已注册的 Driver 类的清单。当调用方

法 getConnection ()时,它将检查清单中的每个驱动程序,直到找到可与 URL 中指定的数据库进

行连接的驱动程序为止。Driver 的方法 connect 使用这个 URL 来建立实际的连接。

用户可绕过 JDBC  管理层直接调用 Driver 方法。这在以下的特殊情况下将很有用:当两个

驱动器可同时连接到数据库中,而用户需要明确地选用其中特定的驱动器时。但一般情况下,

让 DriverManager 类处理打开连接将更为简单。

下述代码显示如何打开一个与位于 URL “j d b c : o d b c : w o m b a t” 的数据库的连接。所用的用

户标识符为 “o b o y” ,口令为 “1 2 J a v a” :

String url = "jdbc:odbc:wombat";

Connection con = DriverManager.getConnection(url, "oboy", "12Java");

2. 一般用法的 U R L

由于 URL 常引起混淆,所以先对一般 URL 作简单说明,然后再讨论 JDBC URL。

U R L(统一资源定位符)提供在 Internet 上定位资源所需的信息。可将它想像为一个地址。

URL 的第一部份指定了访问信息所用的协议,后面总是跟着冒号。常用的协议有 “f t p”

(代表”文件传输协议” )和 “h t t p” (代表”超文本传输协议” ) 。如果协议是 “f i l e” ,表示资

源是在某个本地文件系统上而非在 Internet 上(下例用于表示我们所描述的部分;它并非 U R L

的组成部分) 。

f t p : / / j a v a s o f t . c o m / d o c s / J D K - 1 _ a p i d o c s . z i p

h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j d k / C u r r e n t R e l e a s e

f i l e : / h o m e / h a r o l d w / d o c s / b o o k s / t u t o r i a l / s u m m a r y. h t m l

URL 的其余部份(冒号后面的)给出了数据资源所处位置的有关信息。如果协议是 f i l e,

则 URL 的其余部份是文件的路径。对于 ftp 和 http 协议,URL 的其余部份标识了主机并可选地

给出某个更详尽的地址路径。例如,以下是 JavaSoft 主页的 U R L。该 URL 只标识了主机:

h t t p : / / j a v a . s u n . c o m

从该主页开始浏览,就可以进到许多其他的网页中,其中之一就是 JDBC 主页。JDBC 主页

的 URL 更为具体,它看起来类似:

h t t p : / / j a v a . s u n . c o m / p r o d u c t s / j d b c

3. JDBC URL 

JDBC URL 提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之

建立连接。实际上,驱动程序编程员将决定用什么 JDBC URL 来标识特定的驱动程序。用户不

必关心如何来形成 JDBC URL;他们只需使用与所用的驱动程序一起提供的 URL 即可。J D B C

第二章 预 备 知 识计计67 下载的作用是提供某些约定,驱动程序编程员在构造他们的 JDBC URL 时应该遵循这些约定。

由于 JDBC URL 要与各种不同的驱动程序一起使用,因此这些约定应非常灵活。

首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。例如, odbc 子协议允许

(但并不是要求)URL 含有属性值。

第二,JDBC URL 应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与

给定数据库对话的 applet 打开数据库连接,而无需要求用户去做任何系统管理工作。

第三, JDBC URL 应允许某种程度的间接性。也就是说,JDBC URL 可指向逻辑主机或数

据库名,而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系

统管理员不必将特定主机声明为 JDBC 名称的一部分。网络命名服务(例如 D N S、NIS 和 DCE )

有多种,而对于使用哪种命名服务并无限制。

JDBC URL 的标准语法如下所示。它由三部分组成,各部分间用冒号分隔:

jdbc:< 子协议 >:< 子名称 >

JDBC URL 的三个部分可分解如下:

jdbc ─ 协议。JDBC URL 中的协议总是 j d b c。

〈子协议〉─ 驱动程序名或数据库连接机制(这种机制可由一个或多个驱动程序支持)的

名称。子协议名的典型示例是 “o d b c” ,该名称是为用于指定 ODBC 风格的数据资源名称的

URL 专门保留的。例如,为了通过 JDBC-ODBC 桥来访问某个数据库,可以如下所示U R L:

URL: jdbc:odbc:fred

本例中,子协议为 “o d b c” ,子名称 “f r e d” 是本地ODBC 数据资源。

如果要用网络命名服务(这样 JDBC URL 中的数据库名称不必是实际名称) ,则命名服务可

以作为子协议。例如,可用如下所示的 URL :

jdbc:dcenaming:accounts-payable

本例中,该 URL 指定了本地 DCE 命名服务应该将数据库名称 “a c c o u n t s - p a y a b l e” 解析为

更为具体的可用于连接真实数据库的名称。

〈子名称〉─ 一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子

名称的子名称(含有驱动程序编程员所选的任何内部语法) 。使用子名称的目的是为定位数据库

提供足够的信息。前例中,因为 ODBC 将提供其余部份的信息,因此用 "fred" 就已足够。然而,

位于远程服务器上的数据库需要更多的信息。例如,如果数据库是通过 Internet 来访问的,则在

JDBC URL 中应将网络地址作为子名称的一部份包括进去,且必须遵循如下所示的标准 URL 命

名约定:

//主机名:端口/子协议

假设 dbnet 是个用于将某个主机连接到 Internet 上的协议,则 JDBC URL 类似:

jdbc:dbnet://"wombat:356/fred" 

4. odbc 子协议

子协议 odbc 是一种特殊情况。它是为用于指定 ODBC 风格的数据资源名称的 URL 而保留

的,并具有下列特性:允许在子名称(数据资源名称)后面指定任意多个属性值。 odbc 子协议

68计计第一部分 JSP    入 门

下载的完整语法为:

jdbc:odbc:< 数据资源名称 >[;< 属性名 >=< 属性值 >]*

因此,以下都是合法的 jdbc:odbc 名称:

jdbc:odbc:qeor7

jdbc:odbc:wombat

jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER

jdbc:odbc:qeora;UID=kgh;PWD=fooey

5. 注册子协议

驱动程序编程员可保留某个名称以将之用作 JDBC URL 的子协议名。当 DriverManager 类

将此名称加到已注册的驱动程序清单中时,为之保留该名称的驱动程序应能识别该名称并与它

所标识的数据库建立连接。例如,odbc 是为 JDBC- ODBC 桥而保留的。假设有个 Miracle 公司,

它可能会将 “m i r a c l e” 注册为连接到其 Miracle DBMS 上的 J D B C驱动程序的子协议,从而使

其他人都无法使用这个名称。

JavaSoft 目前作为非正式代理负责注册 JDBC 子协议名称。要注册某个子协议名称,请发送

电子邮件到下述地址:

jdbc@wombat.eng.sun.com

6. 发送 SQL 语句

连接一旦建立,就可用来向它所涉及的数据库传送 SQL 语句。JDBC 对可被发送的 SQL 语

句类型不加任何限制。这就提供了很大的灵活性,即允许使用特定的数据库语句甚至于非 S Q L

语句。然而,它要求用户自己负责确保所涉及的数据库可以处理所发送的 SQL 语句,否则将自

食其果。例如,如果某个应用程序试图向不支持存储程序的 DBMS 发送存储程序调用,就会失

败并将抛出异常。JDBC 要求驱动程序应至少能提供 ANSI SQL-2 Entry Level 功能才可算是

“符合 JDBC 标准”的。这意味着用户至少可信赖这一标准级别的功能。

JDBC 提供了三个类,用于向数据库发送 SQL  语句。Connection 接口中的三个方法可用于

创建这些类的实例。下面列出这些类及其创建方法:

Statement 由方法 createStatement 所创建。Statement 对象用于发送简单的 SQL 语句。

PreparedStatement  由方法 prepareStatement 所创建。PreparedStatement 对象用于发送带有

一个或多个输入参数( IN 参数)的 SQL 语句。PreparedStatement 拥有一组方法,用于设置 I N

参数的值。执行语句时,这些 IN 参数将被送到数据库中。 PreparedStatement 的实例扩展了

Statement ,因此它们都包括了 Statement 的方法。PreparedStatement 对象有可能比 Statement 对

象的效率更高,因为它已被预编译过并存放在那里以供将来使用。

CallableStatement  由方法 prepareCall 所创建。CallableStatement 对象用于执行 SQL 存储

程序 ─ 一组可通过名称来调用(就像函数的调用那样)的 SQL 语句。CallableStatement 对象从

PreparedStatement 中继承了用于处理 IN 参数的方法,而且还增加了用于处理 OUT 参数和

INOUT 参数的方法。

以下所提供的方法可以快速决定应用哪个 Connection 方法来创建不同类型的 SQL 语句。

createStatement 方法用于。

第二章 预 备 知 识计计69 下载简单的 SQL 语句(不带参数) 。

prepareStatement 方法用于:

带一个或多个 IN 参数的 SQL 语句。

经常被执行的简单 SQL 语句。

prepareCall 方法用于:

调用已存储过程。

7. 事务

事务由一个或多个这样的语句组成:这些语句已被执行、完成并被提交或还原。当调用方

法 commit 或 rollback 时,当前事务即告结束,另一个事务随即开始。

缺省情况下,新连接将处于自动提交模式。也就是说,当执行完语句后,将自动对那个语

句调用 commit 方法。这种情况下,由于每个语句都是被单独提交的,因此一个事务只由一个语

句组成。如果禁用自动提交模式,事务将要等到 commit 或 rollback 方法被显式调用时才结束,

因此它将包括上一次调用 commit 或 rollback 方法以来所有执行过的语句。对于第二种情况,事

务中的所有语句将作为组来提交或还原。

方法 commit 使 SQL 语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的

全部锁。而方法 rollback 将丢弃那些更改。

有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组

合在一个事务中来达到。如果两个更新都是成功的,则调用 commit 方法,从而使两个更新结果

成为永久性的;如果其中之一或两个更新都失败了,则调用 rollback 方法,以将值恢复为进行

更新之前的值。

大多数 JDBC  驱动程序都支持事务。事实上,符合 JDBC  的驱动程序必须支持事务。

DatabaseMetaData 给出的信息描述 DBMS 所提供的事务支持水平。

8. 事务隔离级别

如果 DBMS 支持事务处理,它必须有某种途径来管理两个事务同时对一个数据库进行操作

时可能发生的冲突。用户可指定事务隔离级别,以指明 DBMS 应该花多大精力来解决潜在冲突。

例如,当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时怎么办? 假设

第一个事务被还原后,第二个事务所读取的更改值将是无效的,那么是否可允许这种冲突?

JDBC  用户可用以下代码来指示 DBMS 允许在值被提交前读取该值(”dirty 读取” ) ,其中 c o n

是当前连接:

con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

事务隔离级别越高,为避免冲突所花的精力也就越多。 Connection 接口定义了五级,其中

最低级别指定了根本就不支持事务,而最高级别则指定当事务在对某个数据库进行操作时,任

何其他事务不得对那个事务正在读取的数据进行任何更改。通常,隔离级别越高,应用程序执

行的速度也就越慢(用于锁定的资源耗费增加了,而用户间的并发操作减少了) 。在决定采用什

么隔离级别时,开发人员必须在性能需求和数据一致性需求之间进行权衡。当然,实际所能支

持的级别取决于所涉及的 DBMS 的功能。

当创建 Connection 对象时,其事务隔离级别取决于驱动程序,但通常是所涉及的数据库的

70计计第一部分 JSP    入 门

下载缺省值。用户可通过调用 setIsolationLevel 方法来更改事务隔离级别。新的级别将在该连接过程

的剩余时间内生效。要想只改变一个事务的事务隔离级别,必须在该事务开始之前进行设置,

并在该事务结束后进行复位。不提倡在事务的中途对事务隔离级别进行更改,因为这将立即触

发 commit 方法的调用,使在此之前所作的任何更改成为永久性的。

2.5.4   DriverManager概述

DriverManager 类是 JDBC 的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,

并在数据库和相应驱动程序之间建立连接。另外, DriverManager 类也处理诸如驱动程序登录时

间限制及登录和跟踪消息的显示等事务。

对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法是 D r i v e r M a n a g e r. g e t

C o n n e c t i o n。正如名称所示,该方法将建立与数据库的连接。JDBC 允许用户调用 DriverManager 的

方法 g e t D r i v e r、getDrivers 和 registerDriver 及 Driver 的方法 c o n n e c t。但多数情况下,让

DriverManager 类管理建立连接的细节为上策。

1. 跟踪可用驱动程序

DriverManager 类包含一列 Driver 类,它们已通过调用方法 D r i v e r M a n a g e r.registerDriver 对

自己进行了注册。所有 Driver 类都必须包含有一个静态部分。它创建该类的实例,然后在加载

该实例时 对DriverManager  类进行注册。这样,用户正常情况下将不会直接调用

D r i v e r M a n a g e r. r e g i s t e r D r i v e r;而是在加载驱动程序时由驱动程序自动调用。加载 Driver 类,然

后自动在 DriverManager 中注册的方式有两种:

1) 通过调用方法 C l a s s . f o r N a m e。这将显式地加载驱动程序类。由于这与外部设置无关,因

此推荐使用这种加载驱动程序的方法。以下代码加载类 a c m e . d b . D r i v e r:

Class.forName("acme.db.Driver");

如果将 acme.db.Driver 编写为加载时创建实例,并调用以该实例为参数的 D r i v e r M a n a g e r.

r e g i s t e r D r i v e r(本该如此) ,则它在 DriverManager 的驱动程序列表中,并可用于创建连接。

2)  通过将驱动程序添加到 java.lang.System  的属性 jdbc.drivers  中。这是一个由

DriverManager 类加载的驱动程序类名的列表,由冒号分隔。初始化 DriverManager 类时,它搜

索系统属性 j d b c . d r i v e r s,如果用户已输入了一个或多个驱动程序,则 DriverManager 类将试图

加载它们。以下代码说明程序员如何在 ~/.hotjava/properties 中输入三个驱动程序类(启动时,

HotJava 将把它加载到系统属性列表中) :

jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;

对 DriverManager 方法的第一次调用将自动加载这些驱动程序类。

注意:加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能保证,则调用

方法 Class.forName 显式地加载每个驱动程序就显得更为安全。 这也是引入特定驱动程序的方法,

因为一旦 DriverManager 类被初始化,它将不再检查 jdbc.drivers 属性列表。

在以上两种情况中,新加载的 Driver 类都要通过调用 D r i v e r M a n a g e r.registerDriver 类进行

自我注册。如上所述,加载类时将自动执行这一过程。

由于安全方面的原因, JDBC 管理层将跟踪哪个类加载器提供哪个驱动程序。这样,当

第二章 预 备 知 识计计71 下载DriverManager 类打开连接时,它仅使用本地文件系统或与发出连接请求的代码相同的类加载器

提供的驱动程序。

2. 建立连接

加载 Driver 类并在 DriverManager 类中注册后,它们即可用来与数据库建立连接。当调用

D r i v e r M a n a g e r.getConnection 方法发出连接请求时,DriverManager 将检查每个驱动程序,查看

它是否可以建立连接。

有时可能有多个 JDBC 驱动程序可以与给定的 URL 连接。例如,与给定远程数据库连接时,

可以使用 JDBC-ODBC 桥驱动程序、JDBC 到通用网络协议驱动程序或数据库厂商提供的驱动

程序。在这种情况下,测试驱动程序的顺序至关重要,因为 DriverManager 将使用它所找到的第

一个可以成功连接到给定 URL 的驱动程序。

首先 DriverManager 试图按注册的顺序使用每个驱动程序( jdbc.drivers 中列出的驱动程序

总是先注册) 。它将跳过代码不可信任的驱动程序,除非加载它们的源与试图打开连接的代码的

源相同。

它通过轮流在每个驱动程序上调用方法 D r i v e r. c o n n e c t,并向它们传递用户开始传递给方法

D r i v e r M a n a g e r.getConnection 的 URL 来对驱动程序进行测试,然后连接第一个认出该 URL 的

驱动程序。

这种方法初看起来效率不高,但由于不可能同时加载数十个驱动程序,因此每次连接实际

只需几个过程调用和字符串比较。

以下代码是通常情况下用驱动程序(例如 JDBC-ODBC 桥驱动程序)建立连接所需所有步

骤的示例:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载驱动程序

String url = "jdbc:odbc:fred";

DriverManager.getConnection(url, "userID", "passwd");

2.5.5   一个简单的例子

下面是一个简单的例子,在这个例子中,将利用 J D K自带的J D B C - O D B C桥驱动程序查询一

个Microsoft SQL Server 7.0 自带的例子数据库,并将得到的结果在屏幕上显示出来。

1. 建立O D B C数据源

在Wi n d o w s系统的控制面版中,选择“数据源(O D B C) ” ,如果使用Windows 2000,那么将

在“管理工具”中选择。在系统D S N中,选择“添加” ,如图2 - 2所示。

然后,建立一个名为N o r t h w i n d的数据源,并且设定数据源为需要使用的 SQL Server,这里

假设为本地SQL Server数据源,如果读者的数据源不在本地,请自行修改,如图 2 - 3所示。

然后,在接下来几步中设定缺省数据库为N o r t h w i n d,然后点击“完成” ,建立O D B C数据源,

如图2 - 4所示。

2. 程序代码

在建立数据源以后,就可以开始程序设计工作了,下面的程序建立在一个 Java Application

基础上,使用AW T组件来显示数据库查询的结果,具体的程序代码比较简单,读者应该能看懂。

72计计第一部分 JSP    入 门

下载图2 - 2

图2 - 3

第二章 预 备 知 识计计73 下载图2 - 4

import java.awt.*;

//在使用JDBC之前,必须引入JAVA的SQL包

import java.sql.*;

class JDBCTest extends Frame {

TextArea myTextArea;

public JDBCTest () {

//设定程序的显示界面

super("一个简单的JDBC范例");

setLayout(new FlowLayout());

myTextArea = new TextArea(30,80);

add(myTextArea);

resize(500,500);

show();

myTextArea.appendText("数据库查询中,请等待....../n");

}

void displayResults(ResultSet results) throws SQLException {

74计计第一部分 JSP    入 门

下载//首先得到查询结果的信息

ResultSetMetaData resultsMetaData = results.getMetaData();

int cols = resultsMetaData.getColumnCount();

//将等待信息清除

myTextArea.setText("");

//显示结果

while(results.next()) {

for(int i=1;i<=cols;i++) {

if(i>1)

myTextArea.appendText("/t");

try{

myTextArea.appendText(results.getString(i));

}

//捕获空值时产生的异常

catch(NullPointerException e){

}

}

myTextArea.appendText("/n");

}

}

public boolean handleEvent(Event  evt) {

if (evt.id == Event.WINDOW_DESTROY) {

System.exit(0);

return true;

}

return super.handleEvent(evt);

}

public static void main(String argv[]) throws SQLException,Exception {

//设定查询字串

String queryString = "select * from Customers";

JDBCTest myJDBCTest = new JDBCTest();

//加载驱动程序

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

//建立连接

Connection myConn =

DriverManager.getConnection("jdbc:odbc:Northwind","riso","riso");

第二章 预 备 知 识计计75 下载Statement myStmt = myonn.createStatement();

//执行查询

ResultSet myResults = myStmt.executeQuery(queryString);

myJDBCTest.displayResults(myResults);

//关闭所有打开的资源

myResults.close();

myStmt.close();

myConn.close();

}

}

上面的程序对于了解J a v a语言的读者应该是不难理解的,程序的作用就是:首先建立一个数

据库连接,然后执行一个查询,最后将所得的结果显示在屏幕上。程序的执行结果如图2 - 5所

示。

注意:

1) Class.forName()函数指定了加载的驱动程序。

2) getConnection()函数的三个参数中,第二个参数和第三个参数分别表明登录用的用户名和

密码。

图2 - 5

76计计第一部分 JSP    入 门

下载

下载

第3章 JSP开发平台的建立:Tomcat

自从J S P发布以后,推出了各式各样的J S P引擎。作为世界上用得最多的We b服务器软件—

A p a c h e的Apache Group也在进行J S P的实用研究。最初的软件产品是在A p a c h e的Java Servlet引擎

即A p a c h e J S e r v的基础上实现的G N U J S P,一直到G N U J S P 1 . 0,基本上实现了JSP 1.0标准。另外

还出现了一个被称为G S P的产品,是作为G N U体系的一个服务器端的S c r i p t语言实现的。

G N U J S P基本上是一个A p a c h e J S e r v的附属,它主要是利用S e r v l e t将J S P源文件翻译为一个S e r v l e t

的J a v a语言源文件,然后经J a v a编译器编译后作为S e r v l e t执行,这样做的好处前面已经说明了。

在完成G N U J S P 1 . 0的开发以后,开发组的成员开始考虑在 S U N的J S W D K基础上开发一个可

以直接提供We b服务的J S P服务器,当然同时也支持S e r v l e t。这样,J a k a r t a - To m c a t就诞生了。

作为一个开放源代码的软件,J a k a r t a - To m c a t有着自己独特的优势:

首先,它容易得到。事实上,任何人都可以从互联网上自由地下载这个软件。无论从

h t t p : / / j a k a r t a . A p a c h e . o rg还是从其他网站。

其次,对于开发人员,特别是J a v a开发人员,To m c a t提供了全部的源代码,包括S e r v l e t引擎、

J S P引擎、H T T P服务器. . . . . .,无论是对哪一方面感兴趣的程序员,都可以从这些由世界顶尖的程

序员书写的代码中获得收益。

最后,由于源代码的开放及世界上许多程序员的卓有成效的工作, To m c a t已经可以和大部

分的主流服务器一起工作,而且是以相当高的效率一起工作。如:以模块的形式被载入 A p a c h e,

以I S A P I的形式被载入I I S或P W S,以N S A P I的形式被载入Netscape Enterprise Server. . . . . .。

接下来,读者可以看到:

• 如何安装To m c a t,让它发挥作用。

• 如何让To m c a t和A p a c h e、I I S等一起工作。

• 如何配置To m c a t,让它符合自己的要求。

下面,先来建立一个试验用的J S P页面,读者可以先将以下的代码存为H e l l o Wo r l d . j s p。

<HTML>

<HEAD>

<TITLE>JSP测试页面---HelloWorld!</TITLE>

</HEAD>

<BODY>

<%

out.println("<h1>Hello World!<br>世界,你好!</h1>");

%>

</BODY>

</HTML>

3.1   To m c a t的安装和直接使用

在A p a c h e的j a k a r t a项目的主页上,可以看到有 To m c a t的超连接,在这里可以找到各种版本的下载区域,包括当前的发布( R e l e a s e)版本、开发中的各种版本,其中又分为 Wi n 3 2版本和

L i n u x版本,其实对于完全由 J a v a写成的To m c a t,Wi n 3 2版本和L i n u x版本没有多大区别,比如

L i n u x版本,在S o l a r i s下也没有问题。这里,主要以Wi n 3 2版本作为示例。

注意:在安装使用To m c a t之前,先安装J D K,最好是S u n的J D K 1 . 2 . 2或J D K 1 . 3。

首先,下载j a k a r t a - t o m c a t . z i p包,解压缩到一个目录下,如: “c : / t o m c a t” 。这时,会得到如

下的目录结构:

t o m c a t

- - - j a k a r t a - t o m c a t

| - - - b i n To m c a t执行脚本目录

| - - - c o n f To m c a t配置文件

| - - - d o c To m c a t文档

| - - - l i b To m c a t运行需要的库文件(J A R S)

| - - - l o g s To m c a t执行时的L O G文件

| - - - s r c To m c a t的源代码

| - - - w e b a p p s To m c a t的主要We b发布目录

| - - - w o r k To m c a t的工作目录,

To m c a t将翻译J S P文件到的J a v a文件和c l a s s文件放在这里

在B i n目录下,有一个名为s t a r t u p . b a t的脚本文件,执行这个脚本文件,就可以启动 To m c a t服

务器,不过,在启动服务器之前,还需要进行一些设置。

首先,设置环境变量。

Wi n 9 x在a u t o e x e c . b a t里用set 语句来设定环境变量,如:set TO M C AT_HOME = c:/tomcat。

在w i n n t / w i n 2 0 0 0里可以选择“我的电脑” ,右键点出菜单,选择属性,弹出对话框“系统特

性” ,选择“高级”选项页,然后点按钮“环境变量” ,可以编辑系统的环境变量。

• TO M C AT _ H O M E值:c:/tomcat (用TO M C AT _ H O M E指示t o m c a t根目录。

• JAVA _ H O M E值:c : / j a v a / j d k (用J AVA _ H O M E指示j d k 1 . 3安装目录)。

• CLASSPAT H值:c : / j a v a / j d k / l i b / t o o l s . j a r。

实际上,对于 C L A S S PAT H也可以直接打开 t o m c a t . b a t文件,在中间可以找到好几行 s e t

C L A S S PAT H . . . . . .,将自己希望加入的库文件加入到其中即可。

另外,对于J D K 1 . 3,在中文系统上安装之后,系统注册表会有问题,请用 r e g e d i t打开注册

表查j a v a s o f t,位置为hkey_local_machine -> software -> javasoft -> ,找到“Java 运行时环境”

把它导出到文件 t e m p . r e g . . . .,然后用n o t e p a d编辑它,把“ Java 运行时环境”替换成“ J a v a

Runtime Environment” ,然后导入。

同样,最好也把j a v a s o f t注册表项中的“Java 插件”另外复制一份为“Java Plug-in” 。

接下来就可以执行TO M C AT _ H O M E/ b i n / s t a r t u p . b a t,测试一下To m c a t是否运行正常。

运行We b浏览器,如Netscape Navigator或Internet Explorer。在浏览器的地址栏中键入:

h t t p : / / 1 2 7 . 0 . 0 . 1 : 8 0 8 0。如果看到To m c a t的信息,那么就说明To m c a t已经安装成功了。然后测试

To m c a t的J S P引擎是否正常工作,即将前面建立的 H e l l o Wo r l d . j s p文件拷贝到TO M C AT _ H O M E/ w

78计计第一部分 JSP    入 门

下载e b a p p s / e x a m p l e s / j s p目录下,然后在浏览器的地址栏中键入: h t t p : / / 1 2 7 . 0 . 0 . 1 : 8 0 8 0 / e x a m p l e s / j s p /

H e l l o Wo r l d . j s p,这时候应该可以看到如图3 - 1所示的画面:

图 3 - 1

在启动To m c a t的过程中,可能会遇到一些问题,这里就常见问题进行一些说明。

1) 启动To m c a t失败。出现这种情况时,可能有两种现象:

第一种:执行s t a r t u p . b a t以后没有Ja v a窗口出现。

第二种:有Ja v a窗口出现,但是接着自行退出。

对于第一种情况,很可能是 TO M C AT _ H O M E环境变量设置有问题,打开 s t a r t u p . b a t文件,

观察脚本的写法,一般可以发现问题。

对于第二种情况,可能是当前系统中已经有一个服务器占用了 8 0 8 0端口,这时需要把原先

的服务器关闭,或者利用后面讲的 To m c a t的配置方法修改 To m c a t的服务端口。也可能是由于

C L A S S PAT H设置有误,这时需要检查C L A S S PAT H是否设置正确。

2) 启动To m c a t成功,可以看到首页,但是不能执行J S P脚本。

这种情况一般是由于C L A S S PAT H设置有误或J AVA _ H O M E设置有误,经过试验发现,当使

用Wi n d o w s自带的j a v a . e x e(c : / w i n n t / s y s t e m 3 2或c : / w i n d o w s / s y s t e m)时可能会出现这种问题。

3.2   To m c a t和A p a c h e的配合

作为A p a c h e的一个子项目 j a k a r t a - t o m c a t当然要对A p a c h e提供强有力的支持,在下载的

第3章 J S P开发平台的建立:To m c a t计计79 下载To m c a t压缩包解开后,在TO M C A G T _ H O M E/ c o n f目录下有一个t o m c a t - A p a c h e . c o n f文件,这个

文件并不是To m c a t自己的配置文件,而是提供给A p a c h e用来使To m c a t能够和A p a c h e一起工作的。

实际上,这个文件是在To m c a t的运行过程中自动生成的。

但是,光有这个文件还不能使 A p a c h e和To m c a t一起工作,还需要一个A p a c h e的动态载入库

文件A p a c h e M o d u l e J S e r v. d l l,这个文件也可以在网站h t t p : / / j a k a r t a . A p a c h e . o rg得到,需要说明的

是,对于L i n u x版本的t o m c a t,需要的是m o d _ j s e r v. s o文件,w s 5 0为后缀的文件是L i n u x下的动态

连链库文件。

首先,要得到Apache HTTP服务器。A p a c h e是一个免费而且提供源代码的H T T P服务器,由

于A p a c h e强大的性能和用户可以利用源代码构造自己的 H T T P服务器的特性,A p a c h e及其衍生出

来的产品已经成为世界上应用最多的 H T T P服务器,甚至连著名的 I B M公司为We b s p h e r e应用服

务器提供的IBM HTTP Server也是由A p a c h e改造而来的。

在h t t p : / / j a k a r t a . A p a c h e . o rg可以得到A p a c h e服务器的最新版本,本书写作的时候,最新的发

行版已经到了A p a c h e 1 . 3 . 1 2,而A p a c h e 2 . 0 a 6也已经提供用于测试了。

Wi n d o w s下的A p a c h e版本是一个安装文件,可以轻松地安装在计算机上。而如果在 L i n u x下

使用A p a c h e,那么最好使用源代码包自己进行编译,需要注意的是,编译时需使用选项 e n a b l e -

m o d u l e = s o。

双击A p a c h e _ 1 _ 3 _ 1 2 _ Wi n 3 2 . e x e文件进行安装,缺省安装目录为 C:/Program Files/Apache

G r o u p / A p a c h e,可以修改为自己喜欢的目录。

如果需要修改A p a c h e服务器工作的端口号以及 H T M L发布目录或者其他A p a c h e的参数,那

么可以修改A p a c h e安装目录/ c o n f / h t t p d . c o n f,一般可以修改H T M L发布目录为自己喜欢的目录。

至于端口号,当计算机上还运行有其他 We b服务器时可以修改之,一般Wi n d o w s 9 x的机器上如果

装有P W S就需要修改,而Wi n d o w s N T和Windows 2000的机器上如果装有I I S,也需要修改。对于

没有连接到网络的机器,有时需要设置一下 S e r v e r N a m e。A p a c h e服务器的具体配置请见相关书

籍,这里就不讲了。

另外最好将A p a c h e作为一个服务安装在运行Wi n d o w s N T和Windows 2000的电脑上。这只需

要执行开始→程序→Apache Web Server→Install Apache as a service即可。

打开浏览器,在地址栏中键入h t t p : / / 1 2 7 . 0 . 0 . 1 : A p a c h e运行的端口号,如果能够见到A p a c h e的

欢迎页面,或者是一大堆文件让你选择,就可以认为 A p a c h e服务器已经开始工作了。

Apache HTTP 服务器配置成功以后,就可以着手让 To m c a t和A p a c h e一起工作。首先,将得

到的A p a c h e M o d u l e J S e r v. d l l文件拷贝到A p a c h e安装目录下的m o d u l e s子目录下,L i n u x的用户将

m o d _ j s e r v. s o文件拷贝到 A p a c h e安装目录的 l i b e x e c目录下,然后将 A p a c h e安装目录下的

h t t p d . c o n f文件用文本编辑器打开,在最后面加入下面的指令:

INCLUDE To m c a t _ H o m e / c o n f / t o m c a t . c o n f —对于Wi n d o w s用户。

或INCLUDE To m c a t _ H o m e / c o n f / t o m c a t . c o n f —对于L i n u用户。

上面的To m c a t _ H o m e指的是To m c a t的安装目录。

最后,在h t t p d . c o n f文件中加上一行:LoadModule jserv_module modules/ApacheModuleJServ.

d l l。

80计计第一部分 JSP    入 门

下载对于L i n u x下的用户,一般不需要手动加上LoadModule jserv_module libexec/mod_jserv. s o这

一行,t o m c a t - A p a c h e . c o n f文件已经缺省加上了,如果没加,自行加上即可。

一切就绪以后,重新启动 A p a c h e服务器和 To m c a t,在浏览器的地址栏中键入: h t t p : / /

1 2 7 . 0 . 0 . 1 : A p a c h e运行的端口号/ e x a m p l e s / j s p /,如果能够看到 To m c a t的J S P示例列表,就说明

To m c a t已经和A p a c h e一起工作了。

3.3   To m c a t和I I S的配合

Wi n d o w s平台下最常用的We b服务器无疑是I I S(包括P W S) ,对于I I S,To m c a t也提供了配合

工作的方法,使用这种方法,可以为本来不具有 Java Servlet和J S P功能的I I S增加处理J S P和J a v a

S e r v l e t的功能。

为了使To m c a t和I I S一起工作,首先要得到i s a p i _ r e d i r e c t . d l l,这是一个I I S的插件(P l u g - i n) ,

可以从h t t p : / / j a k a r t a . A p a c h e . o rg /直接下载编译好的版本,也可以自己使用 Visual C++编译得到。

得到以后,放到一个自己喜欢的目录,例如c:/tomcat/Jakarta-tomcat/bin/iis/i386/ 目录下。

另外,在使I I S和To m c a t配合的过程中,还需要用到另外两个 To m c a t的配置文件,一个是

w o r k e r s . p r o p e r t i e s,这个文件定义了To m c a t的工作进程使用的主机和端口。在To m c a t的c o n f目录中

有一个示范性的w o r k e r s . p r o p e r t i e s文件。另一个是u r i w o r k e r m a p . p r o p e r t i e s,这个文件是映射U R L

目录和To m c a t工作进程的。同样,在To m c a t的c o n f目录中有一个示范性的u r i w o r k e r m a p . p r o p e r t i e s

文件。

首先,配置i s a p i _ r e d i r e c t . d l l。

1) 在系统注册表中建立一个新的键值: H K E Y _ L O C A L _ M A C H I N E / S O F T WA R E / A p a c h e

Software Foundation/Jakarta Isapi Redirector/1.0。

2) 添加一个名为e x t e n s i o n _ u r i的字符串值为/ j a k a r t a / i s a p i _ r e d i r e c t . d l l。

3) 添加一个名为l o g _ f i l e的字符串值为c : / t o m c a t / J a k a r t a - t o m c a t / l o g s / i s a p i . l o g。

4) 添加一个名为l o g _ l e v e l的字符串值为d e b u g、i n f o r m、e r r o r、e m e rg中的一个。

5) 添加一个名为w o r k e r _ f i l e的字符串值为

6) c:/tomcat/jakarta-tomcat/conf/workers.properties。

7) 添加一个名为w o r k e r _ m o u n t _ f i l e的字符串值为

8) c:/tomcat/jakarta-tomcat/conf/ uriworkermap.properties。

然后,打开I I S的管理控制台,在需要使用To m c a t提供附加的J S P和Java Servlet服务的We b站点

中添加一个虚拟目录。注意,一定要使用“ j a k a r t a”作为虚拟目录的名称,这个虚拟目录的实际

物理位置应当是包含i s a p i _ r e d i r e c t . d l l文件的目录,这里假设为c : / t o m c a t / J a k a r t a - t o m c a t / b i n / i i s / i 3 8 6。

在设定虚拟目录时注意要设此虚拟目录为可执行。如果是在P W S中,一样处理。

接着,在I I S的控制台中为此We b站点添加一个I S A P I过滤器(在此We b站点上点击鼠标右键,

选择属性) 。名称随意,但过滤器要设定为i s a p i _ r e d i r e c t . d l l这个文件。如果使用的是P W S就比较

麻烦了。需要使用注册表编辑器,在键 H K E Y _ L O C A L _ M A C H I N E / S y s t e m / C u r r e n t C o n t r o l S e t / S e

r v i c e s / W 3 S V C / P a r a m e t e r s中,有一个名为Filter Dlls的键值,在这个键值中添加i s a p i _ r e d i r e c t . d l l,

注意要包含完整的路径。

第3章 J S P开发平台的建立:To m c a t计计81 下载最后,重新启动I I S或P W S,最好是能够重新启动计算机。

启动To m c a t以后,打开浏览器,在地址栏中键入 h t t p : / / 1 2 7 . 0 . 0 . 1 / e x a m p l e s /,如果能够看到

j s p和s e r v l e t两个目录,就说明利用i s a p i _ r e d i r e c t . d l l所作的重定向已经成功,可以执行一下 j s p目

录下的例子做试验。

3.4   To m c a t的配置和常见问题

To m c a t为用户提供了一系列的配置文件来帮助用户配置自己的 To m c a t,和Apache HTTP不

同,To m c a t的配置文件主要是基于X M L的;如s e r v e r. x m l、w e b . x m l . . . . . .,只有w o r k e r s . p r o p e r t i e s

和u r i w o r k e r m a p . p r o p e r t i e s等少数几个文件是传统的配置文件。本节将详细讨论 To m c a t的主要配

置文件以及如何利用这些配置文件解决常见问题。

3.4.1   To m c a t的主配置文件:s e r v e r. x m l

观察s e r v e r. x m l,可以发现其中有如表0一些元素。

表3 - 1

元 素 描 述

S e r v e r 是S e r v e r元素是s e r v e r. x m l文件的最高级别的元素,S e r v e r元素描述一

个To m c a t服务器,一般来说用户不用关心这个元素。一个 S e r v e r元素

是一般会包括L o g g e r和C o n t e x t M a n a g e r两个元素

L o g g e r 是L o g g e r元素定义了一个日志对象,一个日志对象包含有如下属性:

是1) name。表示这个日志对象的名称。

是2) path。表示这个日志对象包含的日志内容要输出到哪一个日志文件。

是3) v e r b o s i t y L e v e l。表示这个日志文件记录的日志的级别。

是一般来说,L o g g e r对象是对Java Servlet、J S P和To m c a t运行期事件

的记录

C o n t e x t M a n a g e r 是C o n t e x t M a n a g e r定义了一组C o n t e x t I n t e r c e p t o r s(C o n t e x t M a n a g e的

事件监听器), RequestInterceptors(C o n t e x t M a n a g e的事件监听器) ,

C o n t e x t s(We b应用程序的上下文目录)和它们的 C o n n e c t o r s(连接

器)的结构和配置。

是C o n t e x t M a n a g e r包含如下一些属性:

是1) debug。记录日志记录调试信息的等级。

是2) home。w e b a p p s /、c o n f /、l o g s /和所有C o n t e x t的根目录信息。这

个属性的作用是从一个不同于TO M C AT _ H O M E的目录启动To m c a t。

3) workDir。To m c a t工作目录。

ContextInterceptor 和R e q u e s t I n t e r c e p t o r 是两者都是监听C o n t e x t M a n a g e r的特定事件的拦截器。C o n t e x t I n t e r c e p t o r

监听To m c a t的启动和结束事件信息。而R e q u e s t I n t e r c e p t o r监听用户对服

务器发出的请求信息。一般用户无需关心这些拦截器,对于开发人员,

需要了解这就是全局性的操作得以实现的方法

C o n n e c t o r 是C o n n e c t o r(连接器)对象描述了一个到用户的连接,不管是直接

由To m c a t到用户的浏览器还是通过一个We b服务器。To m c a t的工作进

程和由不同的用户建立的连接传来的读 /写信息和请求/答复信息都是

由连接器对象管理的。对连接器对象的配置中应当包含管理类、

T C P / I P端口等内容

82计计第一部分 JSP    入 门

下载(续)

元 素 描 述

C o n t e x t 是每一个C o n t e x t都描述了一个To m c a t的We b应用程序的目录。这个

对象包含以下属性:

是1) docBase。这是C o n t e x t的目录。可以是绝对目录也可以是基于

C o n t e x t M a n a g e的根目录的相对目录。

是2) path。这是C o n t e x t在We b服务时的虚拟目录位置和目录名。

是3) debug。日志记录的调试信息记录等级。

是4) reloadable。这是为了方便S e r v l e t的开发人员而设置的,当这个

属性开关打开的时候,To m c a t将检查S e r v l e t是否被更新而决定是否自

动重新载入它

1. 加入自己的日志文件

添加L o g g e r对象就可以加入自己的日志文件,添加工作相当简单,只需要将作为示例的

L o g g e r对象复制一份,然后修改一下前面介绍的几个属性就可以了。在设定了 L o g g e r以后,就

可以在自己的S e r v l e t中使用S e r v l e t C o n t e x t . l o g ( )方法来建立自己的日志文件。

2. 设定新的J S P目录

设立新的J S P工作目录是比较简单的,只需要添加一个 C o n t e x t对象就可以了。如,要在

c : / j s p目录下开发J S P项目,并且让用户可以使用/ m y b o o k /虚拟目录访问,则:

<Context path="/mybook" docBase="c:/jsp" debug="0" reloadable="true" > 

</Context>

一般来说,这样就可以直接执行 J S P文件了,如果进一步想要在这下面建立 We b应用程序,

那么还需要进一步的配置,具体方法在后面论述。

3.4.2   Wi n d o w s下代码保护的问题

在Wi n d o w s下使用To m c a t时有一个问题需要注意,可以做一个试验,启动 To m c a t后,在浏

览器的地址栏中键入:http://127.0.0.1:8080/examples/jsp/ HelloWo r l d . J S P(注意后缀要大写) 。

就会发现奇怪的现象,浏览器的窗口中什么都没有,查看 H T M L源文件就会发现,这个J S P文件

的源代码被To m c a t完全输出到了浏览器!如果是这样,岂不是服务器端的任何源代码都会被暴

露在互联网上。

实际上,解决方法很简单,把各种后缀的组合全部写到 To m c a t _ H o m e / c o n f / w e b . x m l里就可

以了,这样t o m c a t会将不同后缀名的j s p分开对待,就不会泄露代码了。

<servlet-mapping>

<servlet-name>

jsp

</servlet-name>

<url-pattern>

*.jsp

</url-pattern>

</servlet-mapping>

<servlet-mapping>

第3章 J S P开发平台的建立:To m c a t计计83 下载<servlet-name>

Jsp

</servlet-name>

<url-pattern>

*.Jsp

</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>

JSp

</servlet-name>

<url-pattern>

*.JSp

</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>

JsP

</servlet-name>

<url-pattern>

*.JsP

</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>

JSP

</servlet-name>

<url-pattern>

*.JSP

</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>

jSp

</servlet-name>

<url-pattern>

*.jSp

</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>

jSP

</servlet-name>

<url-pattern>

*.jSP

</url-pattern>

</servlet-mapping>

<servlet-mapping>

84计计第一部分 JSP    入 门

下载<servlet-name>

jsP

</servlet-name>

<url-pattern>

*.jsP

</url-pattern>

</servlet-mapping>

3.4.3   Apache、I I S和To m c a t协作时工作目录的添加

1. Apache

由于J a k a r t a - To m c a t项目是A p a c h e的一个子项目,所以向To m c a t - A p a c h e协作的We b服务器添

加工作目录时只需要修改 To m c a t - A p a c h e . c o n f文件就可以了。也许读者会觉得奇怪, To m c a t -

A p a c h e . c o n f文件不是在To m c a t启动时自动生成的吗?的确如此,但是 To m c a t自动生成的To m c a t -

A p a c h e . c o n f文件仅仅是To m c a t提供的一个缺省配置文件而已,如果需要,可以修改,然后存放

在另外的目录中,或是更名,再在h t t p d . c o n f文件中将这个新的文件包含进来就可以了。

为什么To m c a t不自动修改To m c a t - A p a c h e . c o n f文件以适应工作目录添加的需要呢? To m c a t的

确修改了To m c a t - A p a c h e . c o n f文件,但是修改的结果显然是不正确的。如,前面在 s e r v e r. x m l中

添加了工作目录/ m y b o o k - - > c : / j s p后,To m c a t修改To m c a t - A p a c h e . c o n f文件,添加了这么几行:

Alias /mybook C:/tomcat/jakarta-tomcat/webapps/mybook

<Directory "C:/tomcat/jakarta-tomcat/webapps/mybook">

Options Indexes FollowSymLinks

</Directory>

ApJServMount /mybook/servlet /mybook

<Location /mybook/Web-INF/ >

AllowOverride Non

deny from all

</Location>

这显然是有问题的,尽管虚拟目录是 / m y b o o k,但是实际的目录并不是 C : / t o m c a t / j a k a r t a -

t o m c a t / w e b a p p s / m y b o o k。查看To m c a t的源代码C : / t o m c a t / j a k a r t a - t o m c a t / s r c / o rg / A p a c h e / t o m c a t / t a s k /

A p a c h e C o n f i g . j a v a文件可以发现,To m c a t在生成To m c a t - A p a c h e . c o n f文件的时候,简单地在虚拟

目录前面加上原先To m c a t的缺省w e b a p p目录作为新的工作目录:

pw.println("Alias " + path + " " + 

FileUtil.patch(tomcatHome + "/webapps" + path));

pw.println("<Directory /"" +

FileUtil.patch(tomcatHome + "/webapps" + path) +

"/">");

pw.println("    Options Indexes FollowSymLinks");

pw.println("</Directory>");

那么如何解决这个问题呢?修改 To m c a t的源代码也可以,不过,对于一般的用户,如前所

述直接修改To m c a t - A p a c h e . c o n f文件更现实一些。修改的方法举例如下:

Alias /mybook C:/jsp

第3章 J S P开发平台的建立:To m c a t计计85 下载<Directory "C:/jsp">

Options Indexes FollowSymLinks

</Directory>

ApJServMount /mybook/servlet /mybook

<Location /mybook/Web-INF/ >

AllowOverride None

deny from all

</Location>

也就是简单地将原先错误的实际目录 C : / t o m c a t / j a k a r t a - t o m c a t / w e b a p p s / m y b o o k修改为c : / j s p

就可以了。

修改完文件后,一定要存为另一个文件并修改 h t t p d . c o n f文件将这个文件包含进来,否则,

重新启动 To m c a t后,这个正确的 To m c a t - A p a c h e . c o n f文件会被 To m c a t重新生成的 To m c a t -

A p a c h e . c o n f文件覆盖掉。最后,重启A p a c h e和To m c a t就可以了。

2. IIS

与A p a c h e和To m c a t几乎无缝的配合不一样, I I S和To m c a t的配合多少有些复杂,需要向

ISAPI Redirect添加新的内容。不过还好, To m c a t的u n i w o r k e r m a p . c o n f文件将这个过程简单化

了。

需要做的事情共分两步:

1) 向To m c a t中添加一个工作目录。前面已经讲述了如何实现,这里依然使用 / m y b o o k - -

> c : / j s p这个例子。

2)  向ISAPI Redirect添加工作目录。使用文本编辑器打开文件 u n i w o r k e r m a p . c o n f,添加一

行:

/mybook/*=ajp12

然后重新启动I I S和To m c a t就可以了。

3.4.4   设定To m c a t作为Wi n d o w s的服务而启动

手工启动To m c a t显然不是一个合适的使用To m c a t作为We b服务的方法,在L i n u x下可以通过

修改启动脚本自动启动To m c a t,在Wi n d o w s下则可以设定To m c a t作为Wi n d o w s的服务而启动。

To m c a t作为Windows NT/2000的一个服务是需要借助工具的

1) 下载工具,这里作为例子的是gservany ----将N T下的一般应用程序作为服务运行的工具。

下载网址为 h t t p : / / w w w. a d v o k . c o m / g s e r v a n y. h t m l。将z i p文件解压缩,将 g s e r v a n y. e x e放入

w i n n t / s y s t e m 3 2目录下,(以防以后被误删)。

2)  在N T的C o m m a n d(命令行模式)下输入: gservany -i tomcat “C : / j a k a r t a - t o m c a t / b i n”

“s t a r t u p . b a t” “C : / j a k a r t a - t o m c a t / b i n” “s h u t d o w n . b a t” 。

3) 启动s e r v i c e管理器,会看到tomcat service被装上,加些注释说明这个s e r v i c e实际干什么,

再修改启动类型为“自动” 。然后再启动它。

这样,就成功地将To m c a t作为S e r v i c e安装在N T下了。

其实,将N T下的应用程序作为服务安装在 N T中的工具还有很多,任何一种都应该可以将

86计计第一部分 JSP    入 门

下载To m c a t加入到N T的服务中。

3.4.5   在To m c a t中建立新的We b应用程序

J S P主要是为建立We b网站而开发的技术,这种技术由We b应用程序的一整套We b文件(j s p,

s e r v l e t,h t m l,j p g,g i f,c l a s s . . . . . .)所组成。To m c a t为We b应用程序的建立提供了一系列的帮助,

下面分步骤描述。

1. 应用程序的目录和结构

按照To m c a t的规范,从/ e x a m p l e例子目录来看,To m c a t的We b应用程序应该由如表3 - 2所示

目录组成的。

表3 - 2

*.html, *.jsp, etc. 这这里可以有许多目录,由用户的网站结构而定,实现的功能应该是网站的界面,也就

是用户主要的可见部分。除了 H T M L文件、J S P文件外,还有j s(J a v a S c r i p t)文件和c s s

(样式表)文件以及其他多媒体文件等等

We b - I N F / w e b . x m l 这这是一个We b应用程序的描述文件。这个文件是一个 X M L文件,描述了S e r v l e t和这个

We b应用程序的其他组件信息,此外还包括一些初始化信息和安全约束等等

We b - I N F / c l a s s e s / 这这个目录及其下的子目录应该包括这个 We b应用程序的所有S e r v l e t文件,以及没有被

压缩打入J A R包的其他c l a s s文件和相关资源。注意,在这个目录下的 J a v a类应该按照其所

属的包组织目录

We b - I N F / l i b / 这这个目录下包含了所有压缩到 J A R文件中的类文件和相关文件。比如:第三方提供的

J a v a库文件、J D B C驱动程序等等. . . . . .

2. web.xml文件

w e b . x m l文件包含了描述整个 We b应用程序的信息。下面以一个 w e b . x m l文件为例,讲解里

面的各个对象。

web.xml:

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

<!-

Web应用程序的主要描述

-->

<display-name>My Web Application</display-name>

<description>

在这里加入Web应用程序的描述信息

</description>

<!-

下面定义了Web应用程序的初始化参数,

在JSP或Servlet文件中使用下面的语句

来得到初始化参数

String value =

getServletContext().getInitParameter("name");

这里可以定义任意多的初始化参数

-->

第3章 J S P开发平台的建立:To m c a t计计87 下载<context-param>

<param-name>webmaster</param-name>

<param-value>myaddress@mycompany.com</param-value>

<description>

这里包含了初始化参数的描述

</description>

</context-param>

<!-

下面的定义描述了组成这个Web应用程序的

Servlet,还包含初始化参数。在Tomcat中,也

可以将放在Web-INF/classes中的Servlet直接以

servlet/Servlet名访问,但是一般来说,不推荐这

样使用。而且这样的使用方法还会导致Servlet的

相关资源组织的复杂性。所以一般来说推荐将所

有的Servlet在这里定义出来。初始化参数可以在

Servlet中一这种语句的到:

String value =

getServletConfig().getInitParameter("name");

-->

<servlet>

<servlet-name>controller</servlet-name>

<description>

这里加入这个Servlet的描述

</description>

<servlet-class>com.mycompany.mypackage.ControllerServlet</servlet-class>

<init-param>

<param-name>listOrders</paramName>

<param-value>com.mycompany.myactions.ListOrdersAction</param-value>

</init-param>

<init-param>

<param-name>saveCustomer</paramName>

<param-value>com.mycompany.myactions.SaveCustomerAction</param-value>

</init-param>

<!-

服务器启动后这个Servlet加载的时间

-->

<load-on-startup>5</load-on-startup>

</servlet>

<servlet>

<servlet-name>graph</servlet-name>

<description>

这个Servlet的描述

</description>

</servlet>

<!-

Servlet映射对应了一个特殊的URI请求

到一个特殊的Servlet的关系

-->

88计计第一部分 JSP    入 门

下载<servlet-mapping>

<servlet-name>controller</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>graph</servlet-name>

<url-pattern>/graph</url-pattern>

</servlet-mapping>

<!-

设定缺省的Session过期时间

-->

<session-config>

<session-timeout>30</session-timeout>    <!-- 30 minutes -->

</session-config>

</web-app>

3. 将应用程序打包为WA R文件

WA R文件是包装We b应用程序的一种方法,使用 WA R文件,既方便了管理各种相关文件,

又减小了整个应用程序的体积。

下面先来看一看将We b应用程序打包为WA R文件的语法:

packager -webArchive[-classpath servletorjspbean/classes [ -

classFiles package/MyClass1.class: package/MyClass2.class ] ] 

<content-root> [-contentFiles login.jsp:index.html:images/me.gif] 

web.xml myWebApp.war

下面是一个简单的应用示例,将 m y We b P a g e . x m l的配置和m y We b P a g e D i r /下的文件打入包

m y We b P a g e . w a r中:

packager -webArchive myWebPageDir myWebPage.xml myWebPage.war 

使用 -contentFiles 标志可以添加个别的目录文件

packager -webArchive myWebPageDir -contentFiles Hello.jsp 

myWebPage.xml myWebPage.war

packager -webArchive myWebPageDir -contentFiles Hello.jsp:Hello.html 

myWebPage.xml myWebPage.war 

假定S e r v l e t文件在c l a s s e s / p a c k a g e / S e r v l e t 1 . c l a s s,指定S e r v l e t和J S P文件:

packager -webArchive -classpath classes myWebPageDir -contentFiles 

Hello.jsp myWebPage.xml myWebPage.war. 

下面示例如何仅仅包含package/Servlet1.class 和 packageB/Servlet.class 两个文件到WA R文

件中:

packager -webArchive -classpath classes -classFiles package/

Servlet1.class:packageB/Servlet.class myWebPageDir -contentFiles 

Hello.jsp myWebPage.xml myWebPage.war

最后,需要说明的是,每个 . w a r文件前面的. x m l文件就是前面讲过的w e b . x m l文件。生成的

WA R文件可以直接放在包含We b应用程序的目录下使用。

第3章 J S P开发平台的建立:To m c a t计计89 下载

下载

第4章 JSP的语法和语义

本章将详细介绍J S P的语法和语义(J S P 1 . 1) 。如果读者接触过A S P或P H P,将会发现J S P的

语法稍显复杂;另一方面,如果读者有J a v a语言程序设计的经验,就会觉得J S P相当简单,其实,

作为J 2 E E的成员,J S P本来就已经成为J a v a的一部分了。

在J S P中,主要包含以下内容:

指令。指令提供该页的全局信息,例如,重要的状态,错误处理,是否是s e s s i o n的一部分等。

声明。页面范围的变量和方法的声明。

脚本。嵌入页面内j a v a代码。

表达式。把表达式变成s t r i n g型以便于包含在页面的输出中。

下面将一一介绍。

4.1   通用的语法规则

J S P的页面是由许多的“元素”组成的,本节描述的语法规则对于这些“元素”都是成立的,

所以称之为“通用”的语法规则,也就是这些元素共有的特性。

4.1.1   元素的语法规则

大部分的J S P元素都基于“X M L”的语法,这些基于“X M L”语法的J S P元素一般来说都有

一个包含有元素名称的起始标志,可能还包含有属性设置,一些可选项,和一个结束标志。当

然,J S P页面的起始标记和结束标记一定要在同一个页面中,有的元素也仅仅有一个包含属性设

置的标志,举例如下:

<mytag attr1="attribute value" ...>

body

</mytag>

<mytab attr1="attribute value" .../>

读者会发现,前面讲述的 To m c a t的配置文件s e r v e r. x m l和w e b . x m l中已经使用了这种语法形

式。

脚本元素则使用的是如A S P般的语法:

<%............%>

实际上,每一个J S P页面都应该可以转换为等价的 X M L页面,在下一章中将详细介绍作为

X M L的J S P。

J S P元素的属性也和X M L中定义的属性遵从同样的原则, J S P页面的属性值一定要使用引号

括起来,双引号(” )和单引号(’ )都可以使用。另外,作为替代,也可以使用 & a p o s ;和& q u t o ;来表示双引号和单引号。

4.1.2   JSP中的相对路径

在J S P中,可以使用相对路径来代替绝对路径,在 J S P的语法规范中称之为“U R I” ,感兴趣

的读者可以在R F C 2 3 9 6中找到比较完整的描述,这里举几个例子来说明相对路径的概念:

"jspRelativeTest.jsp"

"/ jspRelativeTest.jsp"

"../ jspRelativeTest.jsp"

在这三行代码中,都假设是在 c : / t o m c a t / J a k a r t a - t o m c a t / w e b a p p s / e x a m p l e s / j s p / t e s t . j s p中使用

上述相对路径。

对于第一行代码来说,显然文件 j s p R e l a t i v e Te s t . j s p的位置应当为: c : / t o m c a t / J a k a r t a -

t o m c a t / w e b a p p s / e x a m p l e s / j s p / j s p R e l a t i v e Te s t . j s p,这是比较容易理解的。但是对于第二行代码就

要注意了,在J S P中,当相对路径以“/”开头的时候,不是相对于网站的根目录,而是相对于包

含这个J S P文件的We b应用程序的根目录,也就应当是: c : / t o m c a t / J a k a r t a - t o m c a t / w e b a p p s /

e x a m p l e s / j s p R e l a t i v e Te s t . j s p。如果读者对We b应用程序的概念还不清楚,请参见前几章中的相关

章节。第三行代码对于熟悉 U N I X或D O S命令行方式的读者应该不陌生,这种情况下,文件

j s p R e l a t i v e Te s t . j s p的位置应当为: c : / t o m c a t / J a k a r t a - t o m c a t / w e b a p p s / e x a m p l e s /

j s p / j s p R e l a t i v e Te s t . j s p。

4.2   注释

一般来说,可以认为在J S P页面中包含有两种不同类型的注释:一种是J S P本身的,用于描述

J S P程序代码,另一种是J S P生成的页面的,也就是H T M L(或W M L . . .)的注释,用于描述J S P页面

执行后的结果页面的功能。H T M L页面的注释这里就不说了,下面是J S P本身的注释语法的例子:

<%-- 这是一个JSP的注释 --%>

实际上,由于在 J S P的“脚本代码”部分中,也就是后面会提到的“ S c r i p t l e t”直接使用

“<% ...... %>”包含起来的部分中可以使用 J a v a的语法,所以下面形式的注释也就理所当然的可

以使用了:

<% /*这是一个Scriptlet中的注释 */ %>

<% /**这也是一个Scriptlet中的注释,可以用javadoc从生成的Java文件中提取出注释来 */ %>

4.3   指令

前面已经讲过指令在J S P中的地位,指令一般来说有如下的形式:

<%@ directive {attr="value"} %>

指令的这种语法形式尽管简单明了,但并不是符合 X M L的,第5章将讲述指令的X M L语法。

4.3.1   “p a g e”指令

p a g e指令描述了和页面相关的指示信息。在一个 J S P页面中,p a g e指令可以出现多次,但是

第4章 J S P的语法和语义计计91 下载每一种属性却只能出现一次,重复的属性设置将覆盖掉先前的设置。

p a g e指令的基本语法格式如下:

<%@ page page_directive_attr_list %>

page_directive_attr_list ::= {language=" scriptingLanguage"}

{ extends=" className"}

{ import=" importList"}

{ session="true|false" }

{ buffer="none| sizekb" }

{ autoFlush="true| false" }

{ isThreadSafe="true|false" }

{ info=" info_text" }

{ errorPage=" error_url" }

{ isErrorPage="true|false" }

{ contentType="ctinfo" }

下表4 - 1是对这些属性的解释:

表 4 - 1

属性名和可选值 说 明

language = “j a v a” 说language 变量告诉s e r v e r在文件中将采用哪种语言,在J S P当前的规

范(J S P 1 . 1)中,j a v a是J S P唯一支持的语法

extends = “p a c k a g e . c l a s s” 说extends 变量定义了由J S P页面产生的s e r v l e t的父类,一般来说,这

个属性不会用到,但是当需要实现一些特殊功能时,也是比较方便的

说import 变量和任何一个j a v a程序的第一部分一样。同样,它总是被

放在J S P文件的顶部。i m p o r t变量的值是一系列用逗号分开的列表,表

明想要引入的包和类

说注意:

说java.lang.* 

说j a v a x . s e r v l e t . *

说javax.servlet.jsp.* javax.servlet.http.*

说已经缺省地被J S P引擎引入了

session = “t r u e | f a l s e” 说session 变量的缺省值是t r u e,表示当前页面中将有一个缺省的名为

“s e s s i o n”的对象来表示当前会话。 “s e s s i o n”对象的类型是:

说javax.servlet.http.HttpSession 

b u ffer = “n o n e | 8 k b | s i z e k b” 说决定输出流(o u t对象)是否需要缓冲,缺省值是 8 k b。与a u t o F l u s h

一起使用

autoFlush = “t r u e | f a l s e” 说确定是否自动刷新输出缓冲,如果设成 t r u e,则当输出缓冲区满的

时候,刷新缓冲区而不是抛出一个异常

isThreadSafe = “t r u e | f a l s e” 说缺省值是t r u e,如果多个客户请求发向J S P引擎时,可以一次被处理。

J S P程序员要处理同步时共享的状态,以保证同步时确实是安全的。

说如果i s T h r e a d S a f e被设成f a l s e,则采用单线程模式控制客户端访问该

说这没有使你脱离异常分枝,然而,在 s e r v e r可能的情况下,通过它

的判断,创建该页的多个实例运行以处理多个装入的客户请求。而且,

这无法保证同一个客户端的连续请求定位到同一个 J S P页面的实例上,

在多个页面请求之间的共享资源和状态必须同步

92计计第一部分 JSP    入 门

下载

import = “p a c k a g e . * , p a c k a g e . c l a s s”

例如:

<%@ page import=”j a v a . u t i l . *” % >(续)

属性名和可选值 说 明

info = “t e x t” 说页面信息通过页面的S e r v l e t . g e t S e r v l e t I n f o ( )来获得页面可以被访问

的内容的类型

ErrorPage = “p a t h To E r r o r p a g e” 说给出一个J S P文件的相对路径,这个J S P文件用于处理没被处理的例

外。这个J S P文件要把i s E r r o r P a g e设成t r u e。

说需要注意的是:J S P是通过使用S e r v l e t R e q u e s t对象的s e t A t t r i b u t e ( )方

法将名为j a v a x . s e r v l e t . j s p . j s p E x c e p t i o n的对象存储起来实现的。

说另外,当A u t o F l u s h设为t r u e的时候,在J s p Wr i t e r中的数据刷新到

S e r v l e t R e s p o n s e中以后,任何从J S P文件到错误处理文件的未捕获异常

将无法被正常发送

isErrorPage = “t r u e | f a l s e” 说标志一个页面为错误处理页面。设置为 t r u e时,在这个J S P页面中的

缺省对象e x c e p t i o n将被定义,其值将被设定为呼叫此页面的 J S P页面

的错误对象。缺省为f a l s e

说设置J S P文件和最终文件的m i m e类型和字符集的类型。这一项必须

在文件的前部,任何一个其他字符在文件中出现之前。缺省为:

C o n t e n t Type = “t e x t / h t m l ;

charset = ISO - 8859 -1”

4.3.2   Include指令

i n c l u d e指令的作用是包含另一个文件,其语法相当简单:

<%@ include file="......" %>

在这个指令中应该使用前面讲述的 J S P的相对路径表示法。需要说明的是, J S P还有另外一

种包含其他文件的方法:

<jsp:include page="" />

表4 - 2比较了两者的异同:

表 4 - 2

语 法 状 态 对 象 描 述

<%@ include file=”. . . . . .” % > 编译时包含 静态 擎J S P引擎将对所包含的文件进行

语法分析

<jsp:include page=” ” / > 运行时包含 静态和动态 擎J S P引擎将不对所包含的文件进

行语法分析

4.3.3   taglib指令

t a g l i b指令用于指示这个J S P页面所使用的标签库,标签库的具体用法属于 J S P比较高级的内

容,这里就先不讨论了,先讲述一下基础语法:

<%@ taglib uri=" tagLibraryURI" prefix=" tagPrefix" %>

表4 - 3是对各个属性的解释。

第4章 J S P的语法和语义计计93 下载

C o n t e n t Type = “t e x t / h t m l ;

charset = ISO - 8859 -1”表 4 - 3

属 性 说 明

u r i 描描述这个标签库位置的U R I,可以是相对路径或绝对路径

t a g P r e f i x 描定义了一个指示使用此标签库的前缀,例如将 t a g P r e f i x设为m y P r e f i x时,可以使用

下面的语句来使用此标签库中的m y Ta g标签:

< m y P r e f i x : m y Ta g >

描下面这些前缀已经保留:

描jsp:, jspx:, java:, javax:, servlet:, sun:, 和 s u n w :

描目前,空的t a g P r e f i x将被忽视

4.4   内置对象

为开发的方便,J S P中内置了一些对象,不需要预先声明就可以在脚本代码和表达式中随意使

用,前面已经接触到的s e s s i o n和e x c e p t i o n就是两个内置对象,表4 - 4详细讲述J S P中的这些内置对象:

表 4 - 4

对 象 类 型 描 述 作 用 域

r e q u e s t j a v a x . s e r v l e t . S e r v l e t R e q u e s t的子类 作r e q u e s t(用户请

求期)

response  j a v a x . s e r v l e t . S e r v l e t R e s p o n s e的子类 作p a g e(页面执行

期)

p a g e C o n t e x t j a v a x . s e r v l e t . j s p . P a g e C o n t e x t 作p a g e(页面执行

期)

s e s s i o n j a v a x . s e r v l e t . h t t p . H t t p S e s s i o n 作s e s s i o n(会话

期)

a p p l i c a t i o n j a v a x . s e r v l e t . S e r v l e t C o n t e x t 作a p p l i c a t i o n(整

个We b应用程序运

行期)

out  j a v a x . s e r v l e t . j s p . J s p Wr i t e r 作p a g e(页面执行

期)

c o n f i g j a v a x . s e r v l e t . S e r v l e t C o n f i g 作p a g e(页面执行

期)

p a g e j a v a . l a n g . O b j e c t 作P a g e(页面执行

期)

e x c e p t i o n j a v a . l a n g . T h r o w a b l e 作p a g e(页面执行

期)

94计计第一部分 JSP    入 门

下载

客客户端的请求,通常是 H t t p S e r v l e t -

R e q u e s t的子类,如果客户的请求中有参

数,则该对象就有一个参数列表

客JSP 页面的响应,是H t t p S e r v l e t R e s -

ponse 的子类

客页面的属性和需要通过标准 A P I来访

问的相关对象(本质上是构成服务器环

境来让J S P运行的一些对象) ,以便J S P

引擎来编译页面。但是,不同 s e r v e r对

这些属性和对象的实现方式不同

客解决方案是J S P引擎编译用factory 类返

回的服务器的PageContext 类的实现方

法。P a g e C o n t e x t类和r e q u e s t、response 对

象以及p a g e指令的一些属性(e r r o r p a g e

, s e s s i o n , b u ff e r, a u t o f l u s h)同时被初始化,

同时提供r e q u e s t请求的相关的对象

客HTTP session 是与r e q u e s t联合的对象

客s e r v l e t的环境通过调用g e t S e r v l e t C o n f i g

(). g e t C o n t e x t()方法获得

客代表输出流的对象

客页面的S e r v l e t C o n f i g对象

客指向页面自身的方式(在 j a v a代码中

多以t h i s替代)

客没有被T h r o w a b l e捕获的错误。传向

了e r r o r p a g e的U R I4.5   脚本元素

在J S P中,主要的程序部分就是脚本元素,其中包括三个部分:声明( De c l a r a t i o n) 、表达式

(Ex p r e s s i o n)和代码(Sc r i p t l e t) 。从功能上讲,声明用于声明一个或多个变量,表达式将是一

个完整的语言表达式,而代码部分将是一些程序片断。

三个脚本元素的基本语法都是以一个“< %”开头,而以一个“% >”结尾的。

声明的例子:

<%! this is a declaration %>

代码的例子:

<% this is a scriptlet %>

表达式的例子:

<%= this is an expression %>

脚本元素也具有相应的X M L兼容语法,将在第6章介绍。

4.5.1   声明

J S P中的声明用于声明一个或多个变量和方法,并不输出任何的文本到 o u t输出流去。在声明

元素中声明的变量和方法将在J S P页面初始化时初始化。

语法为:

<%! declaration(s) %>

举例如下:

<%! int i = 0; %>

<%! public String f(int i) { if (i<3) return("..."); ... } %>

实际上,声明变量和方法的语句完全可以放在 S c r i p t l e t中,两者有什么不一样呢?放在

< % ! . . . . . . % >中的声明语句在编译为S e r v l e t的时候将作为类的属性而存在,而放在 S c r i p t l e t中的声

明将在类的方法内部被声明。

4.5.2   表达式

J S P中的表达式可以被看作一种简单的输出形式,需要注意的是,表达式一定要有一个可以

输出的值才行。

语法为:

<%= expression %>

举例如下:

<%= (new java.util.Date()).toLocaleString() %>

4.5.3   脚本代码

所谓脚本代码,就是S c r i p t l e t,也就是J S P中的代码部分,在这个部分中可以使用几乎任何

第4章 J S P的语法和语义计计95 下载J a v a的语法。

语法为:

<% scriptlet %>

举例如下:

<% 

if (Calendar.getInstance().get(Calendar.AM_PM) == Calendar.AM) {

%>

Good Morning

<% 

} else {

%>

Good Afternoon

<% 

}

%>

4.6   动作

动作可以影响输出的文本流,使用、编辑、建立对象。在 J S P中,有一些基本的动作,用户

也可以添加自己的动作,这需要使用标签库的知识。 J S P中的动作是完全基于X M L的,下面就来

看看J S P由哪些标准的动作以及具有哪些属性。

4.6.1   id和s c o p e属性

i d属性和s c o p e属性是每一个J S P动作都具有的属性,其中i d表示一个动作的名称,而s c o p e则

表示一个动作的作用域。

s c o p e作用域的取值如表4 - 5所示。

表 4 - 5

作用域取值 有 效 范 围

p a g e 由j a v a x . s e r v l e t . j s p . P a g e C o n t e x t得到在用户请求此页面的过程中有效

r e q u e s t 由S e r v l e t R e q u e s t . g e t A t t r i b u t e ( n a m e )得到在用户的整个请求过程中有效

s e s s i o n 由H t t p S e s s i o n . g e t Value( name)得到在用户的整个会话期内有效

a p p l i c a t i o n 由ServletContext. getAttribute(name)得到在We b应用程序执行期间有效

4.6.2   标准动作

J S P规范书中规定了一些标准的动作,凡是符合 J S P规范的J S P引擎都应当实现这些标准的动

作,下面将一一介绍J S P 1 . 1中规定的标准动作。

1. <jsp:useBean>

< j s p : u s e B e a n >大概是J S P中最重要的一个动作,使用这个动作, J S P可以动态使用J a v a B e a n s

组件来扩充J S P的功能,由于J a v a B e a n s在开发上及< j s p : u s e B e a n >在使用上的简单明了,使得J S P

96计计第一部分 JSP    入 门

下载的开发过程和以往其他动态网页开发工具有了本质上的区别。尽管 A S P等动态网页技术也可以使

用组件技术,但是由于A c t i v e X控件编写上的复杂和使用上的不方便,实际的开发工作中组件技

术使用得并不多。

< j s p : u s e B e a n >的语法如下:

<jsp:useBean id=" name" scope="page|request|session|application"

typeSpec />

typeSpec  ::= class=" className" |

class=" className" type=" typeName" |

type=" typeName" class=" className" |

beanName=" beanName" type=" typeName" |

type=" typeName" beanName=" beanName" |

type=" typeName"

如果在< j s p : u s e B e a n >中需要加入其他的元素,那么使用下面的语法:

<jsp:useBean id=" name" scope="page|request|session|application"

typeSpec >

body

</jsp:useBean>

这里有几个语法的例子:

<jsp:useBean id="connection" class="com.myco.myapp.Connection" />

<jsp:useBean id="connection" class="com.myco.myapp.Connection">

<jsp:setProperty name="connection" property="timeout" value="33">

/jsp:useBean>

在下面的这个例子中,这个JavaBeans对象具有会话期作用域,并且在当前会话中已经存在了。

<jsp:useBean id="wombat" type="my.WombatType" scope="session"/>

如果这个对象不存在的话,将抛出一个C l a s s C a s t E x c e p t i o n异常。

2. <jsp:setProperty>

< j s p : s e t P r o p e r t y >动作用于向一个J a v a B e a n的属性赋值,需要注意的是,在这个动作中将会

使用到的n a m e属性的值将是一个前面已经使用< j s p : u s e B e a n >动作引入的J a v a B e a n的名字。

表4 - 6说明了在使用< j s p : s e t P r o p e r t y >时的类型转换,不过在客户端请求时使用< j s p : s e t P r o p e r t y >

设定J a v a B e a n的属性可以使用任何类型,J S P文件的执行中也不会自动地进行类型转换。

表 4 - 6

属 性 类 型 由S t r i n g类型转换所使用的方法

b o o l e a n j a v a . l a n g . B o o l e a n . v a l u e O f ( S t r i n g )

B o o l e a n

b y t e j a v a . l a n g . B y t e . v a l u e O f ( S t r i n g )

B y t e

i n t j a v a . l a n g . I n t e g e r. v a l u e O f ( S t r i n g )

I n t e g e r

c h a r j a v a . l a n g . C h a r a c t e r. v a l u e O f ( S t r i n g )

C h a r a c t e r

第4章 J S P的语法和语义计计97 下载(续)

属 性 类 型 由S t r i n g类型转换所使用的方法

d o u b l e j a v a . l a n g . D o u b l e . v a l u e O f ( S t r i n g )

D o u b l e

f l o a t j a v a . l a n g . F l o a t . v a l u e O f ( S t r i n g )

F l o a t

l o n g j a v a . l a n g . L o n g . v a l u e O f ( S t r i n g )

L o n g

< j s p : s e t P r o p e t y >的语法如下:

<jsp:setProperty name=" beanName" prop_expr />

prop_expr ::=  property="*" |

property=" propertyName"|

property=" propertyName" param=" parameterName"|

property=" propertyName" value=" propertyValue"

propertyValue ::= string

表4 - 7是属性及其解释

表 4 - 7

属 性 名 描 述

p r o p e r t y 此此属性表明了需要设定值的J a v a B r e a n属性的名称。

此这里有一个很有意思的特殊的 p r o p e r t y设定:当一个p r o p e r t y设定为“*”时,J S P解释器将

把系统S e r v l e t R e q u e s t对象中的参数一个一个的列举出来,检查这个 J a v a B e a n的属性是否和

S e r v k e t R e q u e s t对象中的参数有相同的名称。如果有,就自动将 S e r v l e t R e q u e s t对象中参数值传

递给相应的J a v a B e a n属性

p a r a m 此这个属性表明了在由系统的R e q u e s t向J a v a B e a n传递参数时具体采用哪一个R e q u e s t。具体到

We b页面,也就是哪一个F o r m

v a l u e 此这个属性表明了需要设定给 J a v a B e a n属性的值,可以是直接赋值,也可以是 S e r v l e t R e q u e s t

对象的一个参数名

下面就< j s p : s e t P r o p e r t y >动作举几个例子:

将S e r v l e t R e q u e s t对象r e q u e s t中的参数全部输入到名为r e q u e s t的J a v a B e a n中:

<jsp:setProperty name="request" property="*" />

将S e r v l e t R e q u e s t对象u s e r中的参数u s e r n m e输入到名为u s e r的J a v a B e a n中:

<jsp:setProperty name="user" property="user" param="username" />

将值“i + 1”计算出来后输入到名为r e s u l t s的J a v a B e a n的属性r o w中:

<jsp:setProperty name="results" property="row" value="<%= i+1 %>" />

3. <jsp:getProperty>

< j s p : g e t P r o p e r t y >动作用于从一个J a v a B e a n中得到某个属性的值,无论原先这个属性是什么

类型的,都将被转换为一个S t r i n g类型的值。

语法如下:

<jsp:getProperty name=" name" property=" propertyName" />

98计计第一部分 JSP    入 门

下载例如:

<jsp:getProperty name="user" property="name" />

4. <jsp:include>

< j s p : i n c l u d e >用于引入一个静态或动态的页面到一个J S P文件中,这动作仅仅和J s p Wr i t e对象

发生关系。

< j s p : i n c l u d e >动作可以包含一个或几个< j s p : p a r a m >子动作用于向要引入的页面传递参数。

语法如下:

<jsp:include page=" urlSpec" flush="true"/>

<jsp:include page=" urlSpec" flush="true">

{ <jsp:param .... /> }

</jsp:include>

属性f l u s h设定是否自动刷新缓冲区,实际上,在当前的 J S P版本(1 . 1)中,f l u s h设为f a l s e

是没有任何意义的。

下面是实例:

<jsp:include page="/templates/copyright.html"/>

5. <jsp:forward>

< j s p : f o r w a r d >用于引导客户端的请求到另一个页面或者是另一个 S e r v l e t去。

< j s p : f o r w a r d >动作可以包含一个或几个 < j s p : p a r a m >子动作,用于向要引导进入的页面传递

参数。

需要注意,当< j s p : f o r w a r d >动作发生的时候,如果已经有文本被写入输出流而且页面没有设

置缓冲,那么将抛出一个I l l e g a l S t a t e E x c e p t i o n的异常。

下面是< j s p : f o r w a r d >的语法:

<jsp:forward page=" relativeURLspec" />

<jsp:forward page=" urlSpec">

{ <jsp:param .... /> }*

</jsp:forward>

举例如下:

<% 

String whereTo = "/templates/"+someValue; 

%>

<jsp:forward page=’<%= whereTo %>’ />

6. <jsp:param>

< j s p : p a r a m >实际上提供了名称与值的一种一一对应关系,在 < j s p : i n c l u d e >、< j s p : f o r w a r d >和

< j s p : p l u g i n >中常常作为子动作使用。

语法为:

<jsp:param name=" name" value=" value" />

第4章 J S P的语法和语义计计99 下载7. <jsp:plugin>

< j s p : p l u g i n >动作为We b开发人员提供了一种在 J S P文件中嵌入客户端运行的 J a v a程序(如:

A p p l e t、J a v a B e a n)的方法。在J S P处理这个动作的时候,将根据客户端浏览器的不同, J S P在执

行以后将分别输出为O B J E C T或E M B E D这两个不同的H T M L之素。

下面是< j s p : p l u g i n >的语法:

<jsp:plugintype="bean|applet"

code=" objectCode"

codebase=" objectCodebase"

{ align=" alignment" }

{ archive=" archiveList" }

{ height=" height" }

{ hspace=" hspace" }

{ jreversion=" jreversion" }

{ name=" componentName" }

{ vspace=" vspace" }

{ width=" width" }

{ nspluginurl=" url" }

{ iepluginurl=" url" } >

{ <jsp:params>

{ <jsp:param name=" paramName" value=" paramValue" /> }+

</jsp:params> }

{ <jsp:fallback> arbitrary_text </jsp:fallback> }

</jsp:plugin>

表4 - 8是对< j s p : p l u g i n >动作的子动作和属性的详细说明:

表 4 - 8

子动作或属性 说 明

< j s p : p a r a m > 设定Java Applet或J a v a B e a n执行所需要的参数

< j s p : f a l l b a c k > 设定当浏览器不支持此项P l u g I n时候应当显示的内容

t y p e 指示这个对象是一个Java Applet还是一个J a v a B e a n

j r e v e r s i o n 指示这个插件对象执行所需要的J R E版本,缺省情况为“1 . 1"

n s p l u g i n u r l 指示对于Netscape Navigator的J R E插件的下载地址(U R L)

i e p l u g i n u r l 指示对于Internet Explorer的J R E插件的下载地址(U R L)

c o d e、c o d e b a s e、a l i g n、a r c h i v e、 和H T M L中的意义一致

h e i g h t、h s p a c e、n a m e、v s p a c e、

t i t l e、w i d t h

100计计第一部分 JSP    入 门

下载

下载

第5章 作为XML的JSP

本章将介绍如何使用标准的 X M L语法来书写一个J S P页面,上一章介绍的非 X M L语法在本

章中都可以找到对应的 X M L形式的语法格式。注意, J S P在将来的发展中无疑将越来越强调

X M L的语法格式,读者可以从最新的J S P语法规范----JavaServer Pages(tm) Specification Ve r s i o n

1.2 - public draft 1 (PD1,也就是公开测试版)中发现这一变化趋势。

5.1   为什么要使用X M L相容的语法形式

为什么要使用X M L相容语法来构架J S P呢?当然不是因为X M L是一个时髦的事物,而是作

为X M L文档的J S P将会得到许多的好处。

例如,一个标准的X M L相容的J S P语法将有助于J S P的开发。

J S P文件的X M L语法使得J S P文件的内容很容易被组织和管理。

可以使用X M L的开发和分析工具来开发和分析J S P,仅仅需要更换D T D文件就可以升级到最

新版本的J S P。

X M L格式统一的语法更容易学习和使用。

5.2   关于文本类型的语法

5.2.1   jsp:root元素

作为X M L文档的J S P文件,有一个< j s p : r o o t >元素作为其根元素,这个根元素同时也是标签

库(t a g l i b)设置命名域的地方。在< j s p : r o o t >中使用x m l n s属性来设置当前使用的J S P版本(通过

D T D文件) 。

例如:

<jsp:root

xmlns:jsp="http://java.sun.com/products/jsp/dtd/jsp_1_0.dtd">

remainder of transformed JSP page

</jsp:root>

5.2.2   公共标识符

作为X M L文档的J S P文件建议使用如下形式的文档类型声明:

<! DOCTYPE root

PUBLIC"-//Sun Microsystems Inc.//DTD JavaServer Pages Version 1.1//EN"

"http://java.sun.com/products/jsp/dtd/jspcore_1_0.dtd">5.3   指令

一般的J S P指令具有如下的形式:

<%@ directive { attr="value" }* %>

可以翻译为如下的X M L语法形式:

<jsp:directive.directive { attr="value" }* />

5.3.1   page指令

作为X M L文档的p a g e指令:

<jsp:directive.page page_directive_attr_list />

举例如下

一般的“p a g e”指令:

<%@ page info="my latest JSP Example V1.1" %>

相应的X M L相容语法:

<jsp:directive.page info="my latest JSP Example V1.1" />

5.3.2   include指令

作为X M L文档的i n c l u d e指令:

<jsp:directive.include file=" relativeURLspec" flush="true|false" />

举例如下

一般的i n c l u d e指令:

<%@ include file="copyright.hmtl" %>

相应的X M L相容语法:

<jsp:directive.include file="htmldocs/logo.html" />

5.3.1   taglib指令

t a g l i b指令如前所述,是被包含在< j s p : r o o t >元素中,以x m l n s属性的形式被定义的。

5.3.1   page指示

作为X M L文档的p a g e指为:

<jsp:directive.page page_directive_attr_list />

举例如下。

一般的p a g e指为:

<%@ page info="my latest JSP Example V1.1" %>

相应的X M L相容语法:

102计计第一部分 JSP    入 门

下载<jsp:directive.page info="my latest JSP Example V1.1" />

5.3.2   include指示

作为X M L文档的i n c l u d e指为:

<jsp:directive.include file=" relativeURLspec" flush="true|false" />

举例如下。

一般的i n c l u d e指令:

<%@ include file="copyright.hmtl" %>

相应的X M L相容语法:

<jsp:directive.include file="htmldocs/logo.html" />

5.3.3   taglib指令

t a g l i b指令如前所述,是被包含在< j s p : r o o t >元素中,以x m l n s属性的形式被定义的。

5.4   脚本元素

J S P的脚本元素包含声明、脚本代码、表达式三个部分,下面分别讲述其相应的X M L语法形式。

5.4.1   声明

声明的X M L形式语法为:

<jsp:declaration> declaration goes here </jsp:declaration>

举例如下:

<%! public String f(int i) { if (i<3) return("..."); ... } %>

相应的X M L语句为:

<jsp:declaration> <![CDATA[ public String f(int i) { if (i<3) return("..."); } ]]>

</jsp:declaration>

下面是相关的D T D文件片断:

<!ELEMENT jsp:declaration (#PCDATA) >

5.4.2   脚本代码

声明的X M L形式语法为:

<jsp:scriptlet> code fragment goes here </jsp:scriptlet>

下面是相关的D T D文件片断:

<!ELEMENT jsp:scriptlet (#PCDATA) >

5.4.3   表达式

声明的X M L形式语法为:

第5章 作为X M L的J S P计计1 0 3 下载<jsp:expression> expression goes here </jsp:expression>

举例如下:

<%= str + i + " " + date%>

相应的X M L语句为:

<jsp:expression>

String str ="aasasda";

int i = 5;

java.util.Date date = new Date();

</jsp:expression>

下面是相关的D T D文件片断:

<!ELEMENT jsp:expression (#PCDATA) >

5.5   如何将一个普通的J S P文件转换为一个X M L文档

使用下面的方法可以将一个普通的J S P文件转换为一个X M L文件:

首先,在文本中加入< j s p : r o o t >,在这个元素中使命名前缀“ j s p”成为文本中标准元素的前

缀。

然后,将所有的“< %”按照前面介绍的方法转换为X M L的相容语法形式。

接着,将“t a g l i b”指示转化为使用< j s p : r o o t >中的“x m l n s”属性来指示。

最后,为每一个非J S P元素的部分建立一个C D ATA元素。

利用表5 - 1可以快速地将一个J S P文件转化成一个X M L文件。

表 5 - 1

JSP   元 素 X M L相应元素

<%@ page ... %> <jsp:directive.page ... />

<%@ taglib ... %> 使用< j s p : r o o t >设定

<%@ include ... %> <jsp:directive.include .../>

<%! ... %> <jsp:scriptlet> .... </jsp:scriptlet>

<% ... %> <jsp:scriptlet> .... </jsp:scriptlet>

<%= .... %> <jsp:expression> .... </jsp:expression>

5.6   JSP1.1的D T D文件

这里是J S P 1 . 1的D T D文件,从中可以对J S P的语法有一个完整的理解。

<!ENTITY % jsp.body "

(#PCDATA

|jsp:directive.page

|jsp:directive.include

|jsp:scriptlet

|jsp:declaration

|jsp:expression

|jsp:include

104计计第一部分 JSP    入 门

下载|jsp:forward

|jsp:useBean

|jsp:setProperty

|jsp:getProperty

|jsp:plugin

|jsp:fallback

|jsp:params

|jsp:param)*

">

<!ELEMENT jsp:useBean %jsp.body;>

<!ATTLIST jsp:useBean

id ID #REQUIRED

class CDATA#REQUIRED

scope (page|session|request|application) "page">

<!ELEMENT jsp:setProperty EMPTY>

<!ATTLIST jsp:setProperty

name IDREF#REQUIRED

propertyCDATA#REQUIRED

value CDATA#IMPLIED

param CDATA#IMPLIED>

<!ELEMENT jsp:getProperty EMPTY>

<!ATTLIST jsp:getProperty

name IREF #REQUIRED

propertyCDATA#REQUIRED>

<!ELEMENTjsp:includeEMPTY>

<!ATTLISTjsp:include

flush (true|false)"false"

page CDATA#REQUIRED>

<!ELEMENT jsp:forward EMPTY>

<!ATTLISTjsp:forward

page CDATA#REQUIRED>

<!ELEMENT jsp:scriptlet (#PCDATA)>

<!ELEMENT jsp:declaration (#PCDATA)>

<!ELEMENT jsp:expression (#PCDATA)>

<!ELEMENT jsp:directive.page EMPTY>

<!ATTLIST jsp:directive.page

languageCDATA"java"

extendsCDATA#IMPLIED

contentTypeCDATA"text/html; ISO-8859-1"

import CDATA#IMPLIED

session(true|false)"true"

buffer CDATA"8kb"

autoFlush(true|false)"true"

isThreadSafe(true|false)"true"

info CDATA#IMPLIED

errorPageCDATA#IMPLIED

isErrorPage(true|false)"false">

第5章 作为X M L的J S P计计1 0 5 下载<!ELEMENT jsp:directive.include EMPTY>

<!ATTLIST jsp:directive.include

file CDATA #REQUIRED>

<!ELEMENT jsp:root %jsp.body;>

<!ATTLIST jsp:root

xmlns:jspCDATA#FIXED "http://java.sun.com/products/jsp/dtd/

jsp_1_0.dtd">

106计计第一部分 JSP    入 门

下载

  • 1
    点赞
  • 1
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值