九五 国家重点电子出版物规划项目 希望计算机知识普及系列
编程高手成长之路 6
Advanced Programming with JavaServer Pages
JSP 高级编程
北京希望电子出版社 总策划 北京大学 com 工作室 创作 黄理 洪亮 曹林有 张勇等 编著
特点
z 指导性和实用性强 z 范例丰富 典型 z 附有丰富的实例源码
重点内容
z JSP 技术与 J2EE 技术 z JSP 技术与 XML 技术
z JDBC 新技术及其在 JSP/Servlet 程序中的应用 z JSP 网络程序设计
2001
内 容 简 介
这是一本面向中 高级编程人员的自学指导书 其重点放在JSP 和其他技术的综合使用方面 全书可
分为四大部分 第一部分是JSP 技术与 J2EE 技术 第一章至第四章 着重介绍J2EE 技术的代表 EJB
技术的基本原理与开发EJB 组件的方法 第二部分是 JSP 技术和 XML 技术 第五章至第八章 主要介
绍了XML 技术与 JSP 技术联合的方式之一 Tag Library 第二部分后面的两章是针对市场上最流行的
两个Tag Library 的详细介绍 读者可以把它当作参考手册来使用 本作品的第三部分是 JDBC 新技术及
其在JSP/Servlet 程序中的应用 第九章和第十章 主要介绍最新的JDBC 技术 如 JDBC 2.0/JDBC 3.0
的新特性 以及鲜为人知而又十分重要的JDBC Optional Pack 本作品的第四部分是 JSP 网络程序设计
着重介绍如何使用sun.net 包 JavaMail API 开发访问各种网络服务的 JSP 程序 本作品四个部分之间互为
关联又相对独立 读者可以选择阅读某一个部分或者是通读全文
本版作品是由经验丰富的使用JSP 组建网站的程序员编著 内文附有丰富的实例源码 供读者学习参
考 全书具有语言简明扼要 内容丰富 范例典型 理论与实践相结合的特点 不但是从事用JSP 进行网
站开发和设计的初 中级读者的自学指导书 同时也可作为社会网页设计或编程培训班的教材
说明 与本书配套的面向初 中级用户的书 JSP 深入编程 也已正式出版 欢迎选购
本版CD 为配套书
系 列 书 名 书 名
总 策 划
文 本 著 作 者 责 任 编 辑
C D 制 作 者
C D 测 试 者
出 版 发 行 者
地 址
经 销
九五 国家重点电子出版物规划项目 希望计算机知识普及系列
编程高手成长之路 6
JSP 高级编程 Advanced Programming with JavaServer Pages 北京希望电子出版社
北京大学com工作室 创作 黄理 洪亮 曹林有 张勇等 编著 马红华
希望多媒体开发中心 希望多媒体测试部 北京希望电子出版社
北京中关村大街26 号 100080
网址: www.bhp.com.cn E-mail: [email protected]
电话: 010-62562329,62541992,62637101,62637102,62633308,62633309 发行 010-62613322-215 门市 010-62613322-308 编辑部
各地新华书店 软件连锁店
排 版 C D 生 产 者 文 本 印 刷 者 开 本 / 规 格 版 次 / 印 次 印 数 本 版 号 定 价
希望图书输出中心 杜海燕 北京中新联光盘有限责任公司 北京双青印刷厂
787 毫米×1092 毫米 1/16 开本 38.75 印张 900 千字 2001 年 10 月第 1 版 2001 年 10 月第 1 次印刷 0001 5000 册
ISBN 7-980007-78-6 55.00 元 本版 CD
说明 凡我社光盘配套图书若有缺页 倒页 脱页 自然破损 本社负责调换
声 声 声
声 明 明 明 明
本电子版不包括第 8 章内容 请参看配套图书相关章节
前 言
JSP JavaServer Pages 是目前十分流行的一种技术 主要运行于开发服务端的脚本程 序和动态生成网站的内容 它与目前同样流行的ASP 技术 PHP 技术是同样性质的 同一 层次的 它们在网站的建设中所起到的作用是一样的 但是JSP 技术与后面两种技术相比 有着十分突出的优越性 关于JSP 技术与 ASP 技术 PHP 技术的比较 我们在书中另有论 述 这里只想强调一点 JSP 技术有 J2EE 平台支持 发展前途不可限量 众所周知 J2EE 平台提供了Java 企业应用编程接口 Java Enterprise APIs 为企业计算以及电子商务应用 系统提供了有关的技术和强大的类库支持 J2EE 平台包含十几种技术 JSP 技术正是其中 的一种核心技术 J2EE 的发展势头十分迅猛 在可以预见的将来 Sun 的 J2EE 平台可能 是唯一可以与微软的.Net 构架相互抗衡的平台 在这个意义上说 基于 J2EE 平台的 JSP 技术与基于.Net 平台的 ASP ASP+技术之争 不正好就是 J2EE 平台与.Net 平台之争的折 射吗 因此 JSP 技术以及它的基础 J2EE 技术十分值得我们去关注 在国外 采用 JSP+J2EE 技术构架电子商务网站已经是大行其道了 应用得十分普遍 在国内 这一项技 术还是方兴未艾 采用这一项技术架构的网站还不多 不过大致的趋势已经出现了 这真 是一个令人兴奋的消息 为了帮助广大读者了解 JSP/J2EE 技术并掌握 JSP/J2EE 技术 我 们编写了 JSP 深入编程 和 JSP 高级编程 这两本书 前者侧重于 JSP 技术的基础知识 与基本应用 后者侧重于JSP 技术和其他技术联合使用 本书正是其中第二本书
本书按顺序讲述了以下知识点 zJavaBeans 的基础知识 zEJB 的结构框架
z会话EJB 的开发 部署 测试 应用
zCMP 模式 BMP 模式的实体 EJB 的开发 部署 测试 应用 zJ2EE 体系结构
zCORBA,RMI,JNDI 技术入门 zXML,XSL,CSS 语法介绍 zWML,XHTML 简介 zXML+JSP 的开发模式
zTag Library 的开发 应用 运行原理 zjavax.servlet.jsp.tagext 包的详细说明 zJRun Tag Library 的使用手册 zJakarta Tag Library 的使用手册 zJDBC 2.0/3.0 新特性介绍
zJDBC Optional Pack 介绍 含 RowSet 包 CachedRowSet 包的介绍 zJSP 网络程序开发 访问 SMTP,FTP,News 等服务 Socket 技术应用介绍 zJavaMail 技术完全指南
总的来说 本书可以分为四大部分 第一部分是JSP 技术与 J2EE 技术 第一章至第四 章 着重介绍J2EE 技术的代表 EJB 技术的基本原理与开发 EJB 组件的方法 第二部
分是JSP 技术和 XML 技术 第五章至第八章 主要介绍了XML 技术与 JSP 技术联合的 方式之一 Tag Library 第二部分后面的两章是针对市场上最流行的两个 Tag Library 的 详细介绍 读者可以把它当作参考手册来使用 本书的第三部分是 JDBC 新技术及其在 JSP/Servlet 程序中的应用 第 9 章和第 10 章 主要介绍最新的 JDBC 技术 如 JDBC 2.0/JDBC 3.0 的新特性 以及鲜为人知而又用处极大的 JDBC Optional Pack 本书的第四部 分是JSP 网络程序设计 着重介绍如何使用 sun.net 包 JavaMail API 开发访问各种网络服 务的JSP 程序 本书这四个部分之间互为关联又相互独立 读者可以单独阅读某一个部分 或者是通读全书
顾名思义 本书不是关于JSP 技术的入门书籍 本书要求读者必须有 JSP,Java 基础 否则阅读起来可能会有很大的困难 作者建议读者不妨参考《JSP深入编程》 因为这两本 书是配套编写的 在知识体系结构上有一定的承接性
本书虽然名为 JSP 高级编程 但是真正涉及到JSP 程序编写技巧方面的章节并不多 这是因为JSP 技术的核心内容很少很少 除了基本语法 编译指令 操作指令和内部对象 以外 就没有别的东西了 要发挥JSP 技术的长处 开发功能强大的 JSP 程序 单单靠 JSP 技 术 本 身 是 不 可 能 的 JSP 技 术 必 须 和 其 他 相 关 的 Java 技 术 结 合 起 来 例 如 JDBC,EJB,RMI,CORBA,JavaMail 等技术 才有可能开发出功能强大的程序 本书重点介绍 的就是上述技术的基本原理和开发方法 至于如何把这些技术和 JSP 技术结合起来 开发 运行于服务端的应用程序与 JSP 程序 书中讲的很少 但是读者应该有这方面的经验 况 且只要明了这些技术的基本原理与开发的方法 把它们和JSP 技术结合起来是一件十分简 单的事情 不需要浪费过多的笔墨去介绍这方面的知识
当你读完本书以后 我们不能够保证你一定能够成为JSP 高手 因为本书提到的技术 虽然很多 但是由于篇幅的关系以及其他的原因 这些技术讲的都很肤浅 只是相当于入 门的水平 读者如果想有更大的进步 最好是深入研究本书所提到的技术 找几个项目来 做 当你能够游刃有余地应用这几种技术于JSP 程序的开发中时 那时你才是真正的精通 JSP 的高手 本书给读者指出努力的方向以及提供入门的知识 剩下的就靠读者自身的努 力了 这就是本书命名为 JSP 高级编程 的原因
本书由北京大学com 工作室组织编写 由于时间仓促 笔者的水平有限 书中的谬误 一定很多 不足之处 请读者指正
本书的成功出版 首先归功于本书的主要作者北大黄理同学 他深厚的计算机理论积 累和丰富的实践经验才使得本书兼具理论指导及实务操作性 其工作的严谨态度以及出色 的语言驾驭功底相信读者在阅读本书时自有体会 其次还有感谢北大洪亮同学 其出色的 工作为本书增色不少 也感谢其他许许多多人辛勤的劳动与无私的帮助 轻易便可以列出 很多 北大计算机系的李积善 水木清华 smth.org 的 javafancy 北大未名站的 javalover 还有ROBBY lz.lan snowleaf 以及可爱的 Rainbow
本书技术支持的联系方式 [email protected]
http //162.105.106.162 8080 注 访问前 需要事先 mail 联系 以便启动服务器
目 录
第一部分 JSP 技术与 J2EE 技术 第 1 章 JavaBeans 组件技术
1.1 什么是 JavaBeans
1.2 JSP 中如何使用 JavaBeans 1.3 JavaBeans 的 Scope 属性 1.4 JavaBeans 应用实例 1.5 本章小结
第 2 章 Enterprise JavaBeans
2.1 EJB 技术简介 2.2 EJB 体系结构(一) 2.3 EJB 体系结构(二) 2.4 如何开发 EJB(一) 2.5 如何开发 EJB(二) 2.6 本章小结
第 3 章 EJB 技术进阶
3.1 实体 EJB 的开发技术之一 CMP EJB 3.2 实体 EJB 的开发技术之二——BMP EJB 3.3 EJB 开发实例 封装数据源
3.4 本章小结
第 4 章 JSP 与 J2EE 分布式处理技术
4.1 J2EE 和分布式处理技术 4.2 远程方法调用 RMI 技术 4.3 CORBA 技术
4.4 JNDI 技术 4.5 本章小结 6
第二部分 JSP 技术和 XML 技术 第 5 章 XML 简介
5.1 XML 简介及其语法规则
5.2 DTD 的书写及实例
目录
5.3 CSS 与 XSL 及其实例 5.4 XHTML 简介
5.5 WML 简介 5.6 本章小结
第 6 章 JSP 与 XML 联合开发技术
6.1 XML 与 JSP 技术联合 6.2 在 JSP 中应用 XML
6.3 javax.servlet.jsp.tagext 包介绍 6.4 Tag Library 开发与应用实例 6.5 本章小结
第 7 章 典型 Tag Library 介绍 JRun Tag Library
7.1 JRun Tag Library 简介 7.2 SQL 标记
7.3 J2EE 标记 7.4 Mail 标记 7.5 XML 标记 7.6 其它标记 7.7 本章小结
第 8 章 典型 Tag Library 介绍 Jakarta Tag Library
8.1 Jakarta Tag Librarys 简介 8.2 Application Tag Library 8.3 BSF Tag Library
8.4 DateTime Tag Library 8.5 Input Tag Library 8.6 JDBC Tag Library 8.7 Mailer Tag Library 8.8 Page Tag Library 8.9 Request Tag Library 8.10 Response Tag Library 8.11 Session Tag Library 8.12 本章小结
第三部分 JDBC 新技术及其在 JSP/Servlet 中的应用
第 9 章 JDBC 2.0/3.0 API 的新特性
9.1 JDBC API 2.0 的新特性
9.2 JDBC API 2.0 简介
9.3 JDBC API 3.0 简介
目录
9.4 附录 JDBC 数据类型和 Java 数据类型的映射关系 9.5 本章小结
第 10 章 JDBC Optional Package
10.1 JDBC Optional Package 是什么 10.2 RowSet 包
10.3 CachedRowSet 包 10.4 数据库连接缓冲池 10.5 JNDI 和 RowSet 10.6 本章小结
第四部分 JSP 网络程序设计
第 11 章 JSP 网络程序开发
11.1 配置服务器 11.2 SMTP 服务 11.3 FTP 服务 11.4 News 服务 11.5 Java Socket 11.6 Telnet 服务 11.7 本章小结
第 12 章 Java Mail API
12.1 Java Mail API 简介 12.2 javax.mail 包
12.3 javax.mail.internet 包
12.4 Sun Protocol Privider API 简介
12.5 使用 Java Mail API 访问 Mail 服务器 12.6 本章小结
附录 1 支持 EJB1.0 技术规范的 EJB 平台 开发工具一览表 附录 2 JDBC Driver 一览表
附录 3 WebLogic 服务器的配置方法
附录 4 本书中所用数据库的数据库结构
参考文献
第一部分 JSP 技术与 J2EE 技术
第 1 章 JavaBeans 组件技术
本章将要向读者介绍JavaBeans 组件技术在 JSP 程序开发中的应用 在 JSP 深入编程 中 我们已经介绍了一点关于 JavaBeans 的知识 但是由于体系结构的原因 我们并没有 深入讨论它 也许有的读者对此还有些遗憾 不过不要紧 这一章就来弥补读者的这个遗 憾 本章中读者需要重点掌握的内容有
z JavaBeans 的属性
z JavaBeans 的事件模型
z JSP 中与 JavaBeans 相关的操作指令的语法与用法
z JavaBeans 的开发流程
z JavaBeans 的 Scope 属性
z JavaBeans 封装数据库操作
1.1 什么是 JavaBeans
1.1.1 JavaBeans 简介
软件开发的真正目的之一是利用在程序编码方面的投资 以便在同一公司或者不同公 司的其他开发中重用程序编码 近年来 编程人员投入大量精力以便建立可重用的软件 可重用的软件组件 早期用在面向对象编程方面中的投资已经在Java C#等编程语言的开 发中充分实现 很多软件可以不用做很大的改变就可以运行在各种平台上
JavaBeans 描述了 Java 的软件组件模型 这个模型被设计成使第三方厂家可以生成和 销售能够集成到其他开发厂家或者其他开发人员开发的软件产品的Java 组件
应用程序开发者可以从开发厂家购买现成的JavaBeans 组件 拖放到集成开发环境的工 具箱中 再将其应用于应用软件的开发 对于 JavaBeans 组件的属性 行为可以进行必要的 修改 测试和修订而不必重新编写和编译程序 在JavaBeans 模型中 JavaBeans 组件可以被 修改或者与其他JavaBeans 组件组合以生成新的 JavaBeans 组件或完整的 Java 应用程序
Java 应用程序在运行时 最终用户也可以通过 JavaBeans 组件设计者或应用程序开发 者所建立的属性存取方法 setXXX 方法和 getXXX 方法 修改 JavaBeans 组件的属性 这 些属性可能是颜色和形状等简单属性 也可能是影响JavaBeans 组件总体行为的复杂属性
JavaBeans 组件模型使得软件可以设计成便于修改和便于升级 每个 JavaBeans 组件都
第一部分 JSP 技术与 J2EE 技术
包含了一组属性 操作和事件处理器 将若干个 JavaBeans 组件组合起来就可以生成设计 者 开发者所需要的特定运行行为 JavaBeans 组件存放于容器或工具库中 供开发者开发 应用程序
JavaBeans 就是一个可以复用软件模型 JavaBeans 在某个容器中运行 提供具体的操 作性能 JavaBeans 是建立应用程序的建筑模块 大多数常用的 JavaBeans 通常是中小型控 制程序 但我们也可以编写包装整个应用程序运行逻辑的JavaBeans 组件 并将其嵌入到 复合文档中 以便实现更为复杂的功能
一般来说 JavaBeans 可以表示为简单的 GUI 组件 可以是按钮组件 游标 菜单等 等 这些简单的JavaBeans 组件提供了告诉用户什么是 JavaBeans 的直观方法 但我们也可 以编写一些不可见的JavaBeans 用于接受事件和在幕后工作 例如访问数据库 执行查询 操作的JavaBeans 它们在运行时刻不需要任何可视的界面 在 JSP 程序中所用的 JavaBeans 一般以不可见的组件为主 可见的JavaBeans 一般用于编写 Applet 程序或者 Java 应用程序 1.1.2 JavaBeans 属性
JavaBeans 的属性与一般 Java 程序中所指的属性 或者说与所有面向对象的程序设计 语言中对象的属性是同一个概念 在程序中的具体体现就是类中的变量 在 JavaBeans 的 设计中 按照属性的不同作用又细分为 4 类 Simple 属性 Index 属性 Bound 属性与 Constrained 属性
Simple 属性
一个 Simple 类型的属性表示一个伴随有一对 getXXX() setXXX()方法的变量 属性 的名称与和该属性相关的getXXX() setXXX()方法相对应 例如 如果有 setX()和 getX() 方法 则暗指有一个名为"X"的属性 如果有一个方法名为 isX() 则通常暗指"X"是一个布 尔类型的属性 请看下面的程序清单1.1(JavaBean1.java)
程序清单 1.1
//File Name:JavaBean1.java //Author:fancy
//Date:2001.3.29
//Note:create a simple javabean
public class JavaBean1 {
String ourString= "Hello";
public JavaBean1() {
}
public void setoutString(String newString)
第 1 章 JavaBeans 组件技术
{
ourString=newString;
}
public String getoutString() {
return ourString;
} }
在程序清单1.1(JavaBean1.java)中 我们定义了一个 JavaBean JavaBean1 其实也 就是定义了一个JavaBean1 类 JavaBean1 有一个名为 outString 的字符串类型的属性 与这 个属性相对应的方法为 setoutString()和 getoutString() 使用这两个方法可以存取 outString 属性的值
Indexed 属性
一个Indexed 类型的 JavaBeans 属性表示一个数组值 使用与该属性相对应的 setXXX() 方法和getXXX()方法可以存取数组中某个元素的数值 同时 我们也可以使用另两个同名 方法一次设置或取得整个数组的值(即属性的值) 请看程序清单 1.2
程序清单 1.2
//File Name:JavaBean2.java //Author:fancy
//Date:2001.3.29
//Note:create a indexed javabean
public class JavaBean2 {
int[] dataSet={1 2 3 4 5 6};
public void JavaBean2() {
}
public void setDataSet(int[] x) {
dataSet=x;
}
public void setDataSet(int index int x) {
dataSet[index]=x;
}
public int[] getDataSet()
第一部分 JSP 技术与 J2EE 技术
{
return dataSet;
}
public int getDataSet(int x) {
return dataSet[x];
} }
在程序清单1.2(JavaBean2.java)中 定义了 JavaBean JavaBean2 JavaBean2 具有属 性dataSet dataSet 属性是一个整型数组 JavaBean2.java 定义了 4 个方法以存取 dataSet 属 性的值 它们分别是 setDataSet(int[] x) setDataSet(int index int x) getDataSet(int x) getDataSet() 其中 setDataSet(int[] x)方法可以一次设定 dataSet 属性的值 getDataSet()方法 可以一次获取dataSet 属性的值 该方法的返回值是一个整型数组 getDataSet(int x)方法可 以获取dataSet 属性中某个指定的元素的值 该方法的返回值为整型数据 与这个方法相对 的方法是setDataSet(int index int x)方法 使用这个方法可以指定 dataSet 属性中某个特定 元素的值
Bound 属性
一个 Bound 类型的 JavaBean 组件的属性具有这样的特性 当该种属性的值发生变化 时 必须通知其它的JavaBeans 组件对象 每次 JavaBeans 组件对象的属性值改变时 这种 属性就引发一个PropertyChange 事件(属性改变事件 在 Java 程序中 事件也被看作是一个 对象) 这个事件中封装了发生属性改变事件的属性名 属性的原值 属性变化后的新值 这个事件将被传递到其它的JavaBeans 组件中 至于接收事件的 JavaBeans 组件对象应该做 什么动作由其自己定义 请看程序清单1.3(JavaBean3.java)
程序清单 1.3
//File Name:JavaBean3.java //Author:fancy
//Date:2001.3.29
//Note:create a bound javabean
import java.beans.*;
public class JavaBean3 {
String ourString= "Hello";
private PropertyChangeSupport changes = new PropertyChangeSupport(this);
public void setString(String newString) {
String oldString = ourString;
ourString = newString;
changes.firePropertyChange("ourString" oldString newString);
第 1 章 JavaBeans 组件技术
}
public String getString() {
return ourString;
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changes.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changes.removePropertyChangeListener(l);
} }
读者对程序清单1.3(JavaBean3.java)的运行逻辑一定感到十分迷惑吧 那好 下面我们 就来详细解释JavaBean3.java 程序的含义 程序首先创建了 PropertyChangeSupport 类型的 对象 changes 这是最关键的一步操作 changes 对象主要用于向监听者对象发送信息 当 前的JavaBean 对象已经发生了属性改变的事件 在 JavaBean3.java 程序中 除了普通的存 取JavaBeans 属性值的 setXXX() getXXX()等方法以外 还定义了如下的方法
public void addPropertyChangeListener(PropertyChangeListener l);
public void removePropertyChangeListener(PropertyChangeListener l);
第 一 个 方 法(addPropertyChangeListener() 方 法 ) 其 实 是 调 用 changes 对 象 的 addPropertyChangeListener()方法 使一个事件监听者对象和当前 JavaBean 对象绑定起来 并把它添加到监听者队列中去 充当当前JavaBean 对象的事件监听者 如果当前 JavaBean 对象发生了属性值改变的事件 那么changes 对象会依次通知监听者队列中的每一个对象 当然也通知了这个事件监听者对象 让它对这个事件做出反映
第二个方法(removePropertyChangeListener()方法)和前者的作用相反 该方法其实是调 用changes 对象的 removePropertyChangeListener()方法 从监听者队列中移除某个特定的事 件监听者对象 此事件监听者对象一旦从监听者队列中删除 那么 changes 对象将不会把 属性值改变的事件通知它 它再也没有办法对属性值发生改变的事件作出响应了
getString()方法可以返回属性值 setString()方法用于设定属性值 setString()方法的代 码如下所示
String oldString = ourString;
ourString = newString;
changes.firePropertyChange("ourString" oldString newString);
在上面的代码中 首先新定义一个字符串oldString 用于保存属性的原值 然后把新 值赋给属性值 这样会产生 JavaBeans 组件属性值改变的事件 最后调用 changes 对象的 firePropertyChange()方法 通知监听者队列里的所有事件监听者对象 当前的 JavaBean 对
第一部分 JSP 技术与 J2EE 技术
象发生了属性值改变的事件 属性的名称 属性的新值 属性的原值 都被作为该方法的 参数 一并传给监听者对象 由它们根据这些信息 对此事件作出响应
Bound 类型的属性就是这样使用的 Constrained 属性
JavaBeans 组件的 Constrained 类型的属性具有这样的性质 当这个属性的值将要发生 变化但是还没有发生变化的时候 与这个属性已经建立了某种监听关系的其它Java 对象可 以 否 决 属 性 值 的 改 变 此 Constrained 类 型的属性的事 件监听者对象 将会通过 抛出 PropertyVetoException 异 常 事 件 来 阻 止 该 属 性 值 的 改 变 读 者 请 看 程 序 清 单 1.4(JavaBean4.java)
程序清单 1.4
//File Name:JavaBean4.java //Author:fancy
//Date:2001.3.29
//Note:create a Constrained javabean
import java.beans.*;
public class JavaBean4 {
private PropertyChangeSupport changes=new PropertyChangeSupport(this);
private VetoableChangeSupport vetos=new VetoableChangeSupport(this);
int ourPriceInCents;
public void setPriceInCents(int newPriceInCents) throws PropertyVetoException
{
int oldPriceInCents=ourPriceInCents;
vetos.fireVetoableChange("priceInCents"
new Integer(oldPriceInCents) new Integer(newPriceInCents));
ourPriceInCents=newPriceInCents;
changes.firePropertyChange("priceInCents"
new Integer(oldPriceInCents) new Integer(newPriceInCents));
}
public void addVetoableChangeListener(VetoableChangeListener l) {
vetos.addVetoableChangeListener(l);
}
public void removeVetoableChangeListener(VetoableChangeListener l)
第 1 章 JavaBeans 组件技术
{
vetos.removeVetoableChangeListener(l);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changes.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changes.removePropertyChangeListener(l);
} }
程序清单 1.4(JavaBean4.java)比起程序清单 1.3(JavaBean3.java)来说 显得更为晦涩难 解 在程序清单1.4 中 定义了一个 JavaBean JavaBean4 它有一个 Constrained 类型的 属性是ourPriceInCents 这是一个整型数据 为什么说它是 Constrained 类型的属性呢?请读 者注意 在程序的开始部分 我们分别定义了 PropertyChangeSupport 类型的对象 changes 和VetoableChangeSupport 类型的对象 vetos changes 对象的作用和程序清单 1.3 中 changes 对象的作用一样 在这里我们就不讨论它的用法了 在这里我们主要讨论vetos 对象的用法
vetos 对象主要用于通知事件否决者对象 某个 JavaBean 对象的属性值将要发生变化 让它们投票表决是否允许这个事件的发生 在JavaBean4.java 中 定义了这样的两个方法 分别是
public void addVetoableChangeListener(VetoableChangeListener l);
public void removeVetoableChangeListener(VetoableChangeListener l);
前者可以往事件否决者对象队列中添加新的事件否决者对象 作为JavaBean4 组件对 象的事件否决者 一旦成为 JavaBean4 对象的事件否决者 就可以在事件发生之前 否决 事件的发生
第二个方法与第一个方法的作用相反 它可以将某个特定的事件否决者对象从事件否 决者对象列表中删除 被删除的事件否决者对象就再也没有权利否决事件的发生 除非它 们再次被添加到事件否决者队列中去
在 JavaBean4.java 程序中 读 者 需 要 特 别 注 意 setPriceInCents()方法的实现 在 setPriceInCents()方法中 首先把 ourPriceInCents 属性的原值给保存下来 然后调用 vetos 对象的 fireVetoableChange()方法 通知事件否决者对象队列中的每一个事件否决者对象 告诉它们 JavaBean4 对象即将发生属性改变的事件 发生此事件的属性是 ourPriceInCents 属性的新值为newPriceInCents 属性的原值为 oldPriceInCents (实际上还没有改变属性值) 事件否决者对象会根据这些信息 投票表决是否允许该事件的发生 如果有任何一个事件 否 决 者 对 象 否 决 了 这 个 事 件 发 生 的 可 能 性 那 么 setPriceInCents() 方 法 将 会 抛 出 PropertyVetoException 异常 程序的运行将会中断 下面的代码将不会执行 也就是说属性 值将会保持原来的值 如果事件否决者不否决事件的发生 那么程序将会继续往下执行
第一部分 JSP 技术与 J2EE 技术
给ourPriceInCents 属性赋上新值 然后 changes 对象调用 firePropertyChange()方法 通知事 件监听者队列中的事件监听者对象 让它们对这个事件作出响应
总之 某个JavaBean 组件对象的 Constrained 类型的属性值可否改变取决于其它的事 件否决者对象是否允许这种改变 允许与否的条件由其它的事件否决者对象在自己的类中 进行定义
注意 事件监听者和事件否决者的区别在于事件监听者不能够否决事件的发生 但是 可以响应事件的发生 而事件否决者正好相反 它可以否决事件的发生 但是 不能够响应事件的发生
1.1.3 JavaBeans 的事件模型
事件处理机制是JavaBeans 体系结构的核心之一 也是 Java 体系结构的核心之一 通 过事件处理机制 我们可以指定一些组件作为事件源 发出可以被系统运行环境或者是其 它组件接收的事件 这样 不同的组件就可在某个应用程序内部真正结合在一起 组件之 间通过事件的发送 传递 接受进行通信 构成一个完整的逻辑应用 从概念上讲 所谓 事件机制 是指一种在 源对象 和 监听者对象 之间 某种状态发生变化时的消息传 递机制 事件有许多不同的用途 例如在Windows 系统中常要处理的鼠标事件 窗口边界 改变事件 键盘事件等 在Java 和 JavaBeans 的事件模型中 则是定义了一个一般的 可 扩充的事件机制 这种机制能够
z 对事件类型和传递的模型的定义和扩充提供一个公共框架 并适合于广泛的应用
z 与Java 语言和环境有较高的集成度
z 事件能被系统运行环境捕获和引发
z 能使其它开发工具采取某种技术在设计时直接控制事件 以及事件源和事件监听 者 事件否决者之间的联系
z 事件机制本身不依赖于复杂的开发工具 特别地 还应当
z 能够发现指定的对象类可以生成的事件
z 能够发现指定的对象类可以观察 监听 到的事件
z 提供一个常规的注册机制 允许动态操纵事件源与事件监听者之间的关系
z 不需要其它的虚拟机和语言即可实现
z 事件源与监听者之间可进行高效 快速的事件传递
下面我们就来简单地介绍JavaBeans 的事件机制是如何运作的 事件模型概述
事件从事件源到事件监听者的传递是通过对监听者对象的 Java 方法调用进行的 对 每个明确的事件的发生 都必须相应地定义一个明确的Java 方法 这些方法都集中在事件 监听者接口中定义 而且这个接口必须要继承java.util.EventListener 接口 也就是说 如果 我们希望监听事件源发生的事件 我们必须首先定义一个事件监听者接口 定义各种各样 的监听方法 以便接收事件源传递来的事件 具体实现了事件监听者接口中一些或全部方
第 1 章 JavaBeans 组件技术
法的类就是事件监听者 伴随着事件的发生 事件源通常把事件及其相应的状态都封装在 事件状态对象中 该对象必须继承自 java.util.EventObject 事件状态对象作为参数被传递 给应该响应该事件的事件监听者的方法中
产生某种特定事件的事件源的特征是 遵从规定的编程格式为事件监听者定义注册方 法 以便把监听者对象加入当前事件源的事件监听者队列中 并接受对指定事件监听者接 口实例的引用 有时 事件监听者不能直接实现事件监听者接口 或者还有其它的额外动 作时 就要在一个事件源与其它一个或多个事件监听者之间插入一个事件适配器类的实例 对象 来建立它们之间的联系 实际上 事件适配器类就相当于一个过滤器 它可以把事 件监听者对象不应该接收的事件或者是不能够接收的事件都过滤掉
事件状态对象 Event State Object
与 事 件 有 关 的 状 态 信 息 一 般 都 封 装 在 一 个 事 件 状 态 对 象 中 这 种 对 象 必 须 是 java.util.EventObject 类的子类 按设计习惯 这种事件状态对象类的名应以 Event 结尾 请 看程序清单1.5 (MouseMovedExamEvent.java)
程序清单 1.5
//File Name: MouseMovedExamEvent //Author:fancy
//Date:2001.3.31
//Note:EventObject---Mouse Moved Event import java.awt.Point;
public class MouseMovedExamEvent extends java.util.EventObject {
protected int x;
protected int y;
public void MouseMovedExampleEvent(Component source Point location) {
super(source);
x = location.x;
y = location.y;
}
public Point getLocation() {
return new Point(x y);
} }
在 程 序 清 单 1.5(MouseMovedExamEvent.java) 中 我 们 定 义 了 一 个 事 件 状 态 对 象 MouseMovedExampleEvent 它代表一个鼠标移动的事件 getLocation()方法可以返回鼠标 目前的位置
第一部分 JSP 技术与 J2EE 技术
事件监听者接口与事件监听者
由于JavaBeans 的事件模型是基于 Java 的方法调用 因而需要一个定义并组织事件操 纵 方 法 的 方 式 在 JavaBeans 事 件 模 型 中 事 件 操 纵 方 法 都 被 定 义 在 继 承 了 java.util.EventListener 接口的事件监听者接口中 按照一般的规律 事件监听者接口的命名 要以 Listener 结尾 任何一个类如果想使用在事件监听者接口中定义的方法都必须扩展这 个接口 并且实现其中定义的方法 果真如此 那么这个类也就是事件监听者 请看程序 清单1.6(ArbitraryObject.java)
程序清单 1.6
//File Name: ArbitraryObject.java //Author:fancy
//Date:2001.3.31
//Note: show JavaBean event model
import java.beans.*;
//定义事件状态对象类
public class MouseMovedExampleEvent extends java.util.EventObject {
// 在此类中包含了与鼠标移动事件有关的状态信息
}
//定义了鼠标移动事件的事件监听者接口
interface MouseMovedExampleListener extends java.util.EventListener {
//在这个接口中定义了鼠标移动事件监听者所应支持的方法 void mouseMoved(MouseMovedExampleEvent mme);
}
//定义事件监听者
class ArbitraryObject implements MouseMovedExampleListener {
public void mouseMoved(MouseMovedExampleEvent mme) {
//代码省略 }
在 程 序 清 单 1.6(ArbitraryObject.java) 中 首 先 定 义 了 事 件 状 态 对 象 类 MouseMovedExampleEvent 在此类中包含了与鼠标移动事件有关的状态信息 接着定义了 事件监听者接口MouseMovedExampleListener 在这个接口中定义了鼠标移动事件监听者所 应支持的方法 mouseMoved() 该方法以 MouseMovedExampleEvent 类型的对象为参数 ArbitraryObject 类扩展了 MouseMovedExampleListener 接口 实现了 mouseMoved 方法 所
第 1 章 JavaBeans 组件技术
以它是事件监听者
注意 程序清单1.5/1.6 只是简单的示例 代码不完整 编译不会通过 也不能够运行 读者务必要注意
事件监听者的注册与注销
为了把各种可能的事件监听者注册到合适的事件源的监听者队列中 建立事件源与事 件监听者间的事件流 事件源必须为事件监听者提供注册和注销的方法 在前面介绍bound 类型的 JavaBeans 属性时 我们已经提到了这两种方法 在实际编程中 事件监听者的注 册和注销方法必须使用标准的设计格式
public void add< ListenerType>(< ListenerType> listener) public void remove< ListenerType>(< ListenerType> listener)
前者用于注册事件监听者对象 后者用于注销事件监听者对象 ListenerType 代表事 件监听者对象的类型
程序清单 1.7
//File Name:EventExam.java //Author:fancy
//Date:2001.3.31
//Note:show JavaBean event model
import java.util.*;
//首先定义了一个事件监听者接口
public interface ModelChangedListener extends java.util.EventListener {
void modelChanged(EventObject e);
}
//定义事件监听者
class ModelChangedEventObject implements ModelChangedListener {
public void modelChanged(EventObject e) {
//代码省略 }
//接着定义事件源类 public class EventExam {
// 定义了一个储存事件监听者的数组
private Vector listeners = new Vector();
第一部分 JSP 技术与 J2EE 技术
//上面设计格式中的<ListenerType>在此处即是下面的ModelChangedListener //把监听者注册入listeners数组中
public void addModelChangedListener(ModelChangedListener mcl) {
listeners.addElement(mcl);
}
//把监听者从listeners中注销
public void removeModelChangedListener(ModelChangedListener mcl) {
listeners.removeElement(mcl);
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);
} } }
在程序清单1.7(EventExam.java)中 展示了一个完整的 JavaBeans 事件模型 程序首先 定 义 了 事 件 监 听 者 接 口 ModelChangedListener 然 后 又 定 义 了 一 个 事 件 监 听 者 ModelChangedEventObject ModelChangedEventObject 类实现了 ModelChangedListener 接口 中 定 义 的 modelChanged() 方 法 由 于 该 事 件 监 听 者 所 监 听 的 是 最 普 遍 的 事 件 对 象 EventObject 因此我们就不必定义事件状态对象了 接下来我们定义了事件源 EventExam
第 1 章 JavaBeans 组件技术
类 EventExam 类使用 Vector 数据类型来存储事件监听者队列 EventExam 类定义了 addModelChangedListener()方法用来往事件监听者队列中添加事件监听者对象(表面上添加 的是事件监听者接口 ModelChangedListener 对象 但在实际上添加的是事件监听者对象 ModelChangedEventObject) removeModelChangedListener()方法可以把事件监听者队列中的 特定的事件监听者对象注销 事件源对象调用notifyModelChanged()方法通知事件监听者发 生了modelChanged 事件 notifyModelChanged()方法的方法体中 使用一个 for 循环结构 遍历 Vector 数据结构中保存的每一个事件监听者接口对象 调用它们的 modelChange()方 法 通知事件监听者 modelChanged 事件已经发生了 并且把事件状态对象 e 传递给这些 事件监听者 这里虽然调用的是ModelChangedListener 接口的 modelChange()方法 但是这 个方法并没有真正实现 所以实际上调用的是ModelChangedEventObject 类的 modelChange() 方法
事件适配器类
事件适配器类是 Java JavaBeans 事件模型中极其重要的一部分 在一些应用场合 事件从事件源到事件监听者之间的传递要通过事件适配器类来 转发 例如 当事件源发 出一个事件 而有几个事件监听者对象都可接收该事件 但只有指定的监听者对象可以做 出反应时 就要在事件源与事件监听者之间插入一个事件适配器类 由适配器类来指定事 件应该是由哪些事件监听者来响应 再由它来转发事件
注意 JavaBeans 的事件模型实际上用的并不多 尤其是应用于 JSP 程序中的 JavaBeans 很少需要响应或者监听某种事件的产生 但是这并不等于这部分的内容不重要 有时候为了纪录JavaBeans 都作了哪些敏感的操作 还是需要利用 JavaBeans 的 事件模型的
1.2 JSP 中如何使用 JavaBeans
JavaBeans 被称为是 Java 组件技术的核心 JavaBeans的结构必须满足一定的命名约 定 JavaBeans 类似于 Windows 下的 ActiveX 控件 它们都能提供常用功能并且可以重复 使用 JavaBeans 可以在 JSP 程序中应用给我们带来了很大的方便 这使得开发人员可 以把某些关键功能和核心算法提取出来 封装成为一个组件对象 增加了代码的重用 率 系统的安全性 比如 我们可以将访问数据库的功能 数据处理功能编写封装为 JavaBeans 组件 然后在某个 JSP 程序中加以调用 JavaBeans 技术与 ActiveX 相比 有 着很大的优越性 例如JavaBeans 的与平台无关性 使得 JavaBeans 组件不但可以运行 于Unix 平台 还可以运行在 Windows 平台下面 而且 JavaBeans 从一个平台移植到另 外的平台上代码不需要修改 甚至不需要重新编译 但是 ActiveX 就不同了 它只能 够应用于Windows 平台 而且它的代码移植性很差 从 Windows 98 平台移植到 NT 平 台就需要重新编译代码 甚至要大幅度改写程序 另一方面 JavaBeans 比 ActiveX 要 容易编写得多 用起来也方便得多 起码 JavaBeans 组件在使用以前不需要注册 而 ActiveX 控件在使用以前必须在操作系统中注册 否则在运行的时候 系统将会报错
本节将介绍在 JSP 程序中如何使用 JavaBeans 组件 要想在 JSP 程序中使用
第一部分 JSP 技术与 J2EE 技术
JavaBeans 组件 必须应用<jsp:useBean> <jsp:setProperty> <jsp:getProperty>等 JSP 的操 作指令 关于这几个操作指令的用法 我们在《JSP深入編程》中已经有所涉及 但是限于 体系结构方面的原因 我们的讨论十分肤浅 而且没有举出具体的例子 这不能不说是一 个缺憾 在这一节中 我们会结合实际的例子 再次详细介绍这三个操作指令的用法 顺 便帮助读者复习一下JSP 的基础知识
1.2.1 <jsp:useBean>操作指令
<jsp:useBean>操作指令用于在 JSP 页面中实例化一个 JavaBean 组件 这个实例化的 JavaBean 组件对象将可以在这个 JSP 程序的其它地方被调用 <jsp:useBean>操作指令的基 本语法形式如下所示
<jsp:useBean id="name" scope="page|request|session|application" typeSpec />
或者
<jsp:useBean id="name" scope="page|request|session|application" typeSpec />
body
</jsp:useBean>
语法参数描述
z id 属性用来设定 JavaBeans 的名称 利用 id 可以识别在同一个 JSP 程序中使用 的不同的JavaBeans 组件实例
z class 属性指定 JSP 引擎查找 JavaBeans 代码的路径 一般是这个 JavaBean 所对应 的Java 类名
z scope 属性用于指定 JavaBeans 实例对象的生命周期 亦即这个 JavaBean 的有效作 用范围 scope 的值可能是 page request session 以及 application 在下面 1.3 节 中 我们会详细讨论这四个属性值的含义与用法
typeSpec 可能是如下的四种形式之一 class="className"
或者
class="className" type="typeName"
或者
beanName="beanName" type=" typeName"
或者
type="typeName"
当 JavaBeans 组件对象被实例化以后 你就可以访问它的属性来定制它 我们要获得 它的属性值 应当使用<jsp:getProperty>操作指令或者是在 JSP 程序段中直接调用 JavaBeans 对象的getXXX()方法 <jsp:getProperty>操作指令的语法形式如下所示
<jsp:getProperty id="Name" property="name" />
使用这个操作指令可以获取将要用到的 JavaBeans 组件实例对象的属性值 实际的值 将会被放在输出语句中
要改变 JavaBeans 的属性 你必须使用<jsp:setProperty>操作指令或者是直接调用 JavaBeans 对象的方法 <jsp:setProperty>操作指令有以下两种语法形式
第 1 章 JavaBeans 组件技术
<jsp:setProperty id="Name" property="*" />
或者
<jsp:setProperty id="Name" property="propertyNumber" value="string" />
前者的功能是根据已提交的表单中的数据 设置这个JavaBean 中相应(JavaBeans 属性 的名称和表单对象的名称要相同)的属性值 后者的功能是把这个 JavaBeans 的指定的属性 设为指定的值
为了能在JSP 程序中使用 JavaBeans 组件 你需要特别注意 JavaBeans 类程序的存放问 题:为了使应用程序服务器能找到 JavaBeans 类 你需要将其类文件放在 Web 服务器的一个 特殊位置 以JSWDK1.0.1 服务器为例 JavaBeans 的类文件(编译好的 class 文件)应该放在 examples\WEB-INF\jsp\beans 目录下或者是 webpages\WEB-INF\jsp\beans 目录下面 在 resin 服务器中则是放在doc\WEB-INF\classes 目录下的 至于 JavaBeans 在其他服务器下的存放 路径 读者可以参考下文的介绍或者相应服务器的开发文档
1.2.2 <jsp:setProperty>操作指令
<jsp:setProperty>操作指令被用于指定 JavaBeans 的某个属性的值 它的语法形式如下 所示:
<jsp:setProperty name="BeanName" PropertyExpr />
PropertyExpr ::= property="*"|
property="PropertyName"|
property="PropertyName" value="PropertyValue"|
property="PropertyName" param="ParameterName"|
语法参数说明
z name name 属性用来指定 JavaBeans 的名称 这个 JavaBeans 必须首先使用
<jsp:useBean>操作指令来实例化
z property property 属性被用来指定 JavaBeans 需要定制的属性的名称 如果 property 属性的值为* 那么会发生什么情况呢?请参考 1.4.2 小节
z value value 属性的值将会被赋给 JavaBeans 的属性
z param param 这个属性的作用很微妙 如果客户端传递过来的参数中 有一个参 数的名字和param 属性的值相同 那么这个参数的值将会被赋给 JavaBean 的属性 所以使用了param 属性就不要使用 value 属性 反之 使用了 value 属性就不要使 用param 属性 这两个属性是互斥的 不过 param 属性必须和 property 属性搭配 使用 否则就不知道该赋值给JavaBeans 的哪一个属性了
我们不提倡读者使用<jsp:setProperty>操作指令 而应该在 JSP 程序段中直接调用 JavaBeans 组件实例对象的 setXXX()方法 因为后者的代码简单 使用起来比较灵活 相 对而言 前一种方法的代码就比较繁琐了 而且灵活性也不好 以param 属性为例 客户 端传递归来的参数值一般不应该直接赋给 JavaBeans 的属性 而应该先转换汉字的内码 再赋值 这一点上param 属性就无能为力了
第一部分 JSP 技术与 J2EE 技术
1.2.3 <jsp:getProperty>操作指令
<jsp:getProperty> 操 作 指 令 搭 配 <jsp:useBean> 操 作 指 令 一 起 使 用 可 以 获 取 某 个 JavaBean 组件对象的属性值 并使用输出方法将这个值输出到页面 <jsp:getProperty>操作 指令的语法形式如下所示
<jsp:getProperty name=”BeanName” Property=”PropertyName” />
语法参数说明
z name 这个属性用来指定 JavaBeans 的名称 这个 JavaBeans 组件对象必须已经使 用<jsp:useBean>操作指令实例化了
z Property Property 用来指定要读取的 JavaBeans 组件对象的属性的名称 实际上 我们也可以在JSP 程序段中直接调用 JavaBeans 对象的 getXXX()方法 来获 取JavaBeans 对象的属性值 我们觉得使用这个方法要比使用<jsp:getProperty>操作指令好 因为前者使用起来比较灵活 而且代码相对比较简单
1.2.4 JavaBeans 的开发流程
在这一小节里 我们将详细讨论如何开发JavaBeans 组件 如何把它用到 JSP 程序的 开发中去 实现一个完整的JavaBeans+JSP 的开发流程
编写JavaBeans 组件
第一步 应该是编写一个 JavaBeans 组件程序 我们这就根据上面介绍的知识 编写 一个十分简单的JavaBeans 程序 请看程序清单 1.8(HelloWorld.java)
程序清单 1.8
//File Name:HelloWorld.java //Author:fancy
//Date:2001.3.26
//Note:use this JavaBean to say hello world!
package test;
public class HelloWorld {
String Hello="hello world I am fancy!";
public void HelloWorld() {
}
public void setHello(String name) {
第 1 章 JavaBeans 组件技术
Hello=name;
}
public String getHello() {
return Hello;
} }
在 程 序 清 单 1.8(HelloWorld.java) 中 我 们 编 写 了 一 个 十 分 简 单 的 JavaBean HelloWorld 它有一个字符串类型的 Hello 属性 用于保存问候信息 在编写 HelloWorld.java 程序时 要注意HelloWorld 类必须显式声明为 public 类型 其次是 package 语句的使用 请看代码行:
package test;
这一行代码指示编译器把编译好的类作为test 包的一部分 HelloWorld.class(类文件) HelloWorld.java(程序文件)文件必须位于 test 文件夹中
编译HelloWorld.java 程序
编写好HelloWorld.java 程序以后 我们应该把它保存到哪里呢?以 JSWDK1.0.1 服务器 为例 应该把它保存到webpages\WEB-INF\jsp\beans\目录下面 我们必须新建一个文件夹 这个文件夹的名字必须和package 语句所指定的包名相同 否则服务器无法找到 JavaBean 的类代码 在本例中 这个文件夹的名字应该是 test 保存好 HelloWorld.java 程序后 使 用javac.exe 程序把它编译为 class 文件
编写JSP 程序
第三步是编写JSP 程序 调用我们在上面的步骤中编写好的 HelloWold 组件 请看程 序清单1.9(useBean.jsp)
程序清单 1.9
<%--
File Name:useBean.jsp Author:fancy
Date:2001.3.26
Note:use javabean to say hello world!
--%>
<jsp:useBean id="hello" scope="page" class="test.HelloWorld" />
<jsp:getProperty name="hello" property="Hello" />
<br>
<%
hello.setHello("Are you want to talk to me?");
%>
第一部分 JSP 技术与 J2EE 技术
<%=hello.getHello()%>
在程序清单1.9(useBean.jsp)中 首先使用<jsp:useBean>操作指令实例化了 HelloWorld 组件对象 在下面的代码中 就可以使用 hello 来引用 HelloWorld 组件对象 读者应该注 意class 属性设为 test.HelloWorld 其中 HelloWorld 代表类的名字 test 有两重含义 第一 HelloWorld 类属于 test 包 第二 HelloWorld 类文件保存在 test 文件夹中 所以 package 语 句指定的包名 保存JavaBeans 类的目标文件夹名 还有<jsp:useBean>操作指令中 class 属 性值点号前的部分 这三个值一定要完全相同才行 否则JSP 服务器都将不能找到相应的 JavaBeans 类
接下来 使用<jsp:getProperty>操作指令获取 HelloWorld 组件对象 Hello 属性的值并把 它输出 然后在JSP 程序段和 JSP 表达式中分别调用 hello 对象的 setHello()方法和 getHello() 方法 设定和获取该对象Hello 属性的值
程序清单1.9 的运行效果如图 1.1 所示
图1.1 useBean.jsp 程序的运行效果
到此为止 一个完整的使用了JavaBeans 组件的 JSP 项目就算开发成功了 这个开发 流程虽然简单 不过凡是需要用到JavaBeans 组件的 JSP 程序的开发 一般都应该遵循这 个流程进行开发
1.2.5 JavaBeans 的保存路径
在这一个小节中 我们将总结JavaBeans 程序在不同的 JSP 服务器平台中的保存路径 在介绍这些知识以前 我们首先讨论JavaBeans 程序的存储格式
JavaBeans 组件被设计出来后 一般是以扩展名为 jar 的压缩格式文件存储 在 jar 文 件中包含了与JavaBeans 有关的信息 并以 MANIFEST 文件指定其中的哪些类是 JavaBeans 的类文件 以jar 文件格式存储的 JavaBeans 程序在网络中传送时极大地减少了数据的传输 数量 并把JavaBeans 运行时所需要的一些资源捆绑在一起 jar 文件其实是一个 zip 文件 把它的扩展名改为zip 就可以使用 Winzip 程序打开它 如何才能创建 jar 文件呢?答案是 使用JDK 的 jar.exe 程序 jar.exe 程序一般位于 JDK 的 bin 目录下面 这是一个命令行程序 它的用法如下
jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...
选项
第 1 章 JavaBeans 组件技术
-c 创建新的归档 -t 列出归档内容的列表
-x 展开归档中的命名的 或所有的 文件 -u 更新已存在的归档
-v 生成详细输出到标准输出上 -f 指定归档文件名
-m 包含来自指定的清单 manifest 文件的清单 manifest 信息 -0 只存储方式 未用 ZIP 压缩格式
-M 不产生所有项的清单 manifest 文件 -i 为指定的 jar 文件产生索引信息
-C 改变到指定的目录 并且包含下列文件 如果一个文件名是一个目录 它将被递归处理
清单 manifest 文件名和归档文件名都需要被指定 按'm' 和 'f'标志指定的 相同顺序
示例1 将两个 class 文件归档到一个名为 'classes.jar' 的归档文件中 jar cvf classes.jar Foo.class Bar.class
示例2 用一个存在的清单 manifest 文件 'mymanifest' 将 foo/ 目录下的所有 文件归档到一个名为 'classes.jar' 的归档文件中
jar cvfm classes.jar mymanifest -C foo/ .
下面我们总结JavaBeans 类在不同的 JSP 服务器平台下面的保存位置 JSWDK1.0.1 服务器
保存路径为
1 webpages\WEB-INF\jsp\beans\folderName 2 examples\WEB-INF\jsp\beans\folderName JRun 3.0 服务器
保存路径为
1 servers\default\default-app\WEB-INF\classes\folderName 2 servers\default\demo-app\WEB-INF\classes\folderName Tomcat 3.1/3.2 服务器
保存路径为
1 webapps\admin\WEB-INF\classes\folderName 2 webapps\examples\WEB-INF\classes\folderName 3 webapps\ROOT\WEB-INF\classes\folderName 4 webapps\test\WEB-INF\classes\folderName
5 webapps\XmlServlet\WEB-INF\classes\folderName
第一部分 JSP 技术与 J2EE 技术
Resin1.2 服务器 保存路径为
1 doc\WEB-INF\classes\folderName
2 doc\examples\..\WEB\INF\classes\folderName
限于篇幅 关于 JavaBeans 程序在各个 JSP 服务器平台下的保存路径 我们就介绍到 这里 如果读者还希望了解 JavaBeans 程序在其他 JSP 服务器平台下的保存路径 请参考 相应的服务器开发文档 或者是与本书作者联系
1.3 JavaBeans 的 Scope 属性
对于JSP 程序而言 使用 JavaBeans 组件不仅可以封装许多信息 而且还可以将一些 数据处理的逻辑隐藏到JavaBeans 的内部 除此之外 我们还可以设定 JavaBeans 的 Scope 属性 使得 JavaBeans 组件对于不同的任务 具有不同的生命周期和不同的使用范围 在 前面 我们已经提到过Scope 属性具有四个可能的值 分别是 application session request page 分别代表 JavaBeans 的四种不同的生命周期和四种不同的使用范围 下面我们就分别 介绍这四种不同的情况
1.3.1 Application Scope
如果JavaBeans 的 Scope 属性被指定为 application 也就是说这个 JavaBean 组件具有 Application Scope 这是什么意思呢?如果一个 JavaBean 组件具有 Application Scope 那么 它的生命周期和JSP 的 Application 对象同步 作用范围也和 Application 对象一样 使用这 种类型的 JavaBeans 组件 可以在多个用户之间共享全局信息 具体来说 它的生命周期 是这样子的 如果某个JSP 程序使用<jsp:useBean>操作指令创建了一个 JavaBean 对象 而 且这个JavaBean 组件具有 Application Scope 那么这个 JavaBean 就一直在服务器的内存空 间中待命 随时处理客户端的请求 直到服务器关闭为止 它所保存的信息才消失 它所 占用的系统资源才会被释放 在此期间 如果有若干个用户请求的 JSP 程序中 需要用到 这个JavaBean 组件 那么服务器在执行<jsp:useBean>操作指令时 并不会创建新的 JavaBean 组件 而是创建源对象的一个同步拷贝 在任何一个拷贝对象上发生的改变都会影响到源 对象 源对象也会做出同步的改变 不过这个状态的改变不会影响其他已经存在的拷贝对 象 这种类型的JavaBeans 组件的功能和 JSP 的 Application 对象十分类似 不过前者的功 能要强大得多 而且可以自由扩展 用起来也方便得多 请看程序清单1.10(Counter.java) 程序清单1.11(useCounter.jsp)
程序清单 1.10 //File Name:Counter.java //Author:fancy
//Date:2001.3.26
//Note:use this JavaBean to Counter!
package test;
第 1 章 JavaBeans 组件技术
public class Counter {
int Count=1;
public void Counter() {
}
public void addCount() {
Count++;
}
public int getCount() {
return Count;
} }
在程序清单1.10 中 我们定义了一个 Counter Bean 这个 JavaBean 组件可以用于记录 访问者的人数 由于这个程序十分简单 我们就不多做介绍了
程序清单 1.11
<%--
File Name:useCounter.jsp Author:fancy
Date:2001.3.26
Note:use javabean to say hello world!
--%>
<jsp:useBean id="counter" scope="application" class="test.Counter" />
<br>
你好 你是第
<%
out.println(counter.getCount());
counter.addCount();
%>位访客
程序清单1.11(useCounter.jsp)中 首先使用<jsp:useBean>操作指令引入了 JavaBean 组 件 Counter 并且声明它的 Scope 为 Application 这一步十分重要 然后调用 Counter 组件的getCount()方法 获取访问过这个 JSP 程序的人数 如果 Counter 组件刚刚被创建 那么这个方法将会返回缺省值 1 接着调用 Counter 组件的 addCounte()方法 把访问人 数加上1
第一部分 JSP 技术与 J2EE 技术
程序清单1.11 的运行效果如图 1.2 所示
图1.2 useCounter.jsp 的运行效果 1.3.2 Session Scope
如果一个JavaBean 组件的 Scope 属性值为 session 那么这个 JavaBean 组件的生命周 期 作用范围就和 JSP 的 Session 对象的生命周期 作用范围一样 也就是说 这一类型 的 JavaBeans 组件的生命周期就是某个会话过程所经历的时间 也许有的读者对会话过程 还不太了解 实际上 会话过程是对于单个用户而言的 会话过程的开始以用户开始访问 某个网站为标志 会话过程的结束以用户结束对该网站的访问为标志 不同的用户对应着 不同的会话过程 不同的会话过程之间互不干涉 互不影响 假设用户A 第一次访问了某 个网站的某个JSP 程序 而这个 JSP 程序用到了一个 Scope 属性为 session 的 JavaBean 组 件 那么服务器会自动创建这个JavaBean 组件的实例对象 并且当 A 用户继续访问同一网 站其他的JSP 程序 而其他的 JSP 程序又用到同一个 JavaBean 对象时 那么服务器不会创 建新的 JavaBean 对象 而是使用已经存在的 JavaBean 对象实例 也就是说在第一个 JSP 程序中创建的JavaBean 组件对象在这个用户访问的同一网站的所有的 JSP 程序中都是可用 的 而且这个JavaBean 组件对象的状态保持唯一性 如果有另一个用户 B 访问了用户 A 访问过的JSP 程序 那么服务器是否会不创建新的 JavaBean 组件对象 而使用由于用户 A 访问而创建的JavaBean 组件对象呢?答案是否定的 服务器将会为用户 B 创建只属于他的 JavaBean 组件对象 这个新创建的 JavaBean 组件对象在用户 B 访问的同一网站的所有 JSP 程序中都是直接可用的 而不需要创建一个新的组件 并且属于用户A 的 JavaBean 组件对 象和属于用户B 的组件对象都是唯一的 它们之间互不干涉 这里我们讨论的只是两个用 户的情况 其实如果有多个用户在线 情况也一样
综上所述 Scope 属性为 session 的 JavaBeans 组件的功能 作用范围都和 JSP 的 Session 对象十分类似 不过前者的功能比后者要强大得多 并且使用起来也灵活得多 具有可扩 展性 后者没有扩展性
下面我们就利用这种类型的 JavaBeans 组件 来编写一个特殊的计数器程序 这个计 数器并不是统计一个网页的访问人数 而是统计一个用户所访问的页面数目 请看程序清 单1.12(beanPage1.jsp) 程序清单 1.13(beanPage2.jsp)
程序清单 1.12
<%--
File Name:beanPage1.jsp
第 1 章 JavaBeans 组件技术
Author:fancy Date:2001.3.26
Note:use Counter to calculate how many pages this user have visited --%>
<jsp:useBean id="counter" scope="session" class="test.Counter" />
<br>
第一页
<br>
你好 你已经访问了
<%
out.println(counter.getCount());
counter.addCount();
%>个页面
在程序清单1.12 中 我们使用的 JavaBean 组件仍然是我们在程序清单 1.10 中编写的 Counter 不过这里 Counter 对象的 Scope 属性值是 session 而不是 Application 当用户首 先调用beanPage1.jsp 程序时 Counter 对象被创建了 程序清单 1.12 的运行效果如图 1.3 所示
图1.3 beanPage1.jsp 程序的运行效果 程序清单 1.13
<%--
File Name:beanPage2.jsp Author:fancy
Date:2001.3.26
Note:use Counter to calculate how many pages this user have visited --%>
<jsp:useBean id="counter" scope="session" class="test.Counter" />
<br>
第二页
<br>
你好 你已经访问了
<%