Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » 技术文章库  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 使用JSP和XML进行Web应用开发
hutuworm





发贴: 48
积分: 40
于 2003-02-24 13:44 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
使用JSP和XML进行Web应用开发

第一部分:JSP概要

作者:Qusay H. Mahmoud

如果你曾经开发过基于通用网关接口(Common Gateway Interface, CGI)和Servlets技术的Web应用,你已经习惯于在一个程序中生成整个页面(静态和动态部分)的Web编程思想。如果你想找到一个解决方案,把静态和动态两部分隔开,不要再找了,JSP就在这里。

JSP页面允许你把前端的表现和业务逻辑(中间层次和后端层次)分开。它是非常好的Web应用快速应用开发(RAD)途径。本系列文章是一部初步教程,讲解如何为今天和明天的市场开发现代Web应用。本文是这一系列的第一篇,介绍JSP技术的概念和优势,接着向你展示如何使用这种令人激动的技术,如何创建可重用组件处理表单(form)。

动态Web

Web以往是一个用于提供静态信息发布的基于网络的超媒体分布式信息系统,现在它已经逐渐发展到用于提供销售、采购货物和提供服务的崭新市场。日益增加的实现这种复杂应用的市场需求,需要一种技术来表现动态信息。

第一代的解决方案中包括了CGI,它是通过Web服务器运行外部程序的一种机制。CGI脚本的问题在于其不可扩展性,因为它对每一个请求必须创建一个新的进程。

第二代的解决方案是Web服务器厂商为他们的服务器提供插件(plug-in)和应用编程接口(API)。问题是它们的解决方案是专用于他们的服务器产品的。例如,微软公司提供的Active Server Pages(ASP),它可以很容易的开发动态内容,但是他们的解决方案仅仅能够在Microsoft IIS或Personal Web服务器上使用。所以,如果你想使用ASP,你就必须忠实于微软的产品,而你不能自由地选择你所钟爱的Web服务器和操作系统。

另一种第二代技术就是在企业级计算领域非常流行的Servlets。Servlets使利用Java技术编写服务器端应用变得非常简单。但是,无论CGI还是Servlet都有一个问题,就是你必须遵循编写、编译和部署的生命周期。

JSP页面是第三代解决方案,你可以很方便的同一些第二代解决方案结合使用,创建动态内容,方便的、快速的开发基于Web应用,并且能够跟众多的其它技术相配合使用:Web服务器,Web浏览器,应用服务器和其它开发工具。

JavaServer Pages(JSP)

JSP技术是一种由Sun公司开发的开放的、免费的规范,用于作为微软公司的ASP技术的替代产品,同时也是Java 2 企业版(J2EE)规范的一个重要组成部分。很多成熟的商用应用服务器(如SunONE Application Server, BEA WebLogic, IBM WebSphere, Live JRun, Orion, 等等)都已经支持JSP技术。

JSP对比ASP

JSP和ASP提供相似的功能。他们都是通过使用标记,允许在HTML页面中嵌入代码,并能实现session跟踪、与数据库连接。一些细节上的区别是:

• ASP是用VBScript编写的,而JSP是用Java编程语言编写的。因此,JSP页面是与平台无关的,而ASP不是。

• JSP页面使用JavaBeans技术作为组件架构,而ASP页面使用ActiveX组件。

除细节区别之外,还有一些重要的差别,可以帮助你决策选择何种技术:

• 速度和可扩展性:尽管ASP页面有缓存机制,它们总是被解释执行的。相反,JSP页面是在第一次被调用时,被编译成Java Servlets并被载入到内存中,为所有后续调用服务。这使JSP页面远比ASP页面有速度和可扩展性方面的优势。

• 可扩展的标记:JSP页面有一个高级功能,叫作可扩展标记。这种机制使开发人员可以创建自定义标记。换句话说,可扩展标记允许你扩展JSP页面的标记语法。使用ASP无法做到这一点。

• 自由的选择:除非安装Chilli!soft ASP,否则ASP页面仅仅能够运行在Microsoft IIS或Personal Web服务器上。使用ASP页面需要对微软产品的忠实,而JSP页面不把你绑定在任何特定的Web服务器或操作系统上。JSP页面正在成为广泛支持的标准。

了解更多JSP页面和ASP页面的比较信息,参见“比较JavaServer Pages(JSP)和微软Active Server Pages技术”。

软件环境

为了运行JSP页面,你需要一个装有遵循JSP和servlet规范的Web容器的Web服务器。Web容器在Web服务器上运行,并管理在这个Web服务器上所有JSP页面和Servlets的执行。Tomcat 3.2.1是一个Java Servlet 2.2和JSP 1.1规范的完全的参考实现。

配置Tomcat:

• 设置环境变量JAVA_HOME指向你安装的J2SE的根目录。

• 设置TOMCAT_HOME环境变量指向你安装的Tomcat的根目录。

• 启动Tomcat,在Windows下使用TOMCAT_HOME/bin/startup.bat,在UNIX下使用startup.sh。缺省情况下,它将监听8080端口。

• 在TOMCAT_HOME/webapps/examples/jsp目录下存放.jsp文件,你的JavaBeans类存放在TOMCAT_HOME/webapps/examples/web-inf/classes目录下。

注意:如果你使用Windows,在你启动Tomcat时,可能会遇到Out of space environment error。解决这个问题有两个方法:修改DOS窗口的初始内存设置,使它的值大于3200,或者编辑config.sys文件,并增加以下这行:

SHELL=C:\PATHTO\command.com /E:4096 /P

JSP页面是如何工作的

一个JSP页面简单来说是传统的HTML加一点Java代码。一个JSP页面的文件扩展名是.jsp而不是.html或.htm,这将告诉服务器这个页面需要由服务器上的扩展或插件来完成特殊的处理。下面是一个简单的例子:

范例 1: date.jsp

<HTML>

<HEAD>

<TITLE>JSP Example</TITLE>

</HEAD>

<BODY BGCOLOR="ffffcc">

<CENTER>

<H2>Date and Time</H2>

<%

java.util.Date today = new java.util.Date();

out.println("Today's date is: "+today);

%>

</CENTER>

</BODY>

</HTML>

这个例子包含传统的HTML和一些Java代码。标记<%表示脚本段(scriptlet)的开始,而%>标记表示脚本段的结束。当date.jsp被从一个Web浏览器中请求时,你将看到与图1相似的东西:


图1:请求date.jsp

现象的背后

在这个页面(date.jsp)被调用时,它将被(JSP引擎)编译成一个Java Servlet。这时,servlet被Servlet引擎象对待其它Servlet一样来处理。Servlet引擎接着会加载Servlet类(通过类加载器,ClassLoader),并执行它以生成将被发送到浏览器的动态HTML,如图2所示。在这个例子中,Servlets创建一个date对象,并把它作为一个字符串写入out对象,out对象是一个指向浏览器的输出流。


图2:调用一个JSP时的请求和响应流程

下一次这个页面被请求时,JSP引擎将执行已经加载的Servlet,除非JSP页面被修改了(在这种情况下,它将被自动重新编译成一个Servlet并执行)。

脚本元素

在date.jsp例子中,使用的是包含了包(package)的名称的完整的Date类名,这样你会觉得很烦。如果你想在创建一个Date的实例时只简单写成:Date today = new Date(); 而不必指明完整的类路径,需要使用page指示符(directive),如下所示:

范例2 :date2.jsp

<%@page import="java.util.*" %>

<HTML>

<HEAD>

<TITLE>JSP Example</TITLE>

</HEAD>

<BODY BGCOLOR="ffffcc">

<CENTER>

<H2>Date and Time</H2>

<%

Date today = new Date();

out.println("Today's date is: "+today);

%>

</CENTER>

</BODY>

</HTML>

还有,实现同一个目的的另一种方式是使用<%=标记,如:

范例 3: date3.jsp

<%@page import="java.util.*" %>

<HTML>

<HEAD>

<TITLE>JSP Example</TITLE>

</HEAD>

<BODY BGCOLOR="#ffffcc">

<CENTER>

<H2>Date and Time</H2>

Today's date is: <%= new Date() %>

</CENTER>

</BODY>

</HTML>

如你所见到的,同一件事情可以用不同的标记和技巧实现。有很多JSP脚本元素,这里有一些习惯性的原则可以帮助你有效地使用JSP脚本元素:

• 使用<% ... %>处理声明、表达式或者任何其它有效的代码片断。例1就是一个例子。

• 象在<%@page ... %>中一样使用page指示符,定义脚本语言。而且它还可以用于指定import语句。这是一个例子:

<%@page language="java"” import="java.util.*"” %>

• 使用<%! .... %>声明变量或方法。例如:

<%! int x = 10; double y = 2.0; %>

• 使用<%= ... %>定以表达式和将结果转换成String。例如:

<%= a+b %> 或 <%= new java.util.Date() %>

• 使用include指示符:<%@ include ... %>是在主JSP文件中,插入另一个文件的内容。例如:

<%@include file="copyright.html" %>

处理表单

电子商务应用中最常用的部分是一个HTML表单,用户在其中填写一些信息,如姓名和地址。使用JSP,表单的数据(用户填入表单的信息)存储在从浏览器发送到JSP容器的request对象中。请求被处理完的结果将通过response对象发送回浏览器。这两个对象在JSP中是预定义的,随时可以使用。

为了演示如何使用JSP处理HTML表单,这里有一个示范表单,它有两个域:一个是名字,一个是电子邮件。象你看到的一样,HTML表单在JSP源文件中被定义。request.getParameter()方法被用于从表单中提取数据,并赋值到使用JSP标记生成的变量中。

process.jsp页面或者显示一个空表单、或者显示用户提供的信息,这由表单中域的数值决定。如果表单的值是null,显示空表单;否则,用户提供的信息将被显示出来。注意,表单的创建和处理都是由同一个JSP文件中的代码完成的。

范例4: process.jsp

<HTML>

<HEAD>

<TITLE>Form Example</TITLE>

</HEAD>

<BODY BGCOLOR="#ffffcc">

<% if (request.getParameter("name")==

null && request.getParameter("email")

== null) { %>

<CENTER>

<H2>User Info Request Form</H2>

<FORM METHOD="GET" ACTION="process.jsp">

<P>

Your name: <input type="text" name=

"name" size=26>

<P>

Your email: <input type="text" name=

"email" size=26>

<P>

<input type="submit" value="Process">

</FORM>

</CENTER>

<% } else { %>

<%! String name, email; %>

<%

name = request.getParameter("name");

email = request.getParameter("email");

%>

<P>

<B>You have provided the following

info</B>:

<P>

<B>Name</B>: <%= name %><P>

<B>Email</B>: <%= email %>

<% } %>

</BODY>

</HTML>

如果process.jsp被Web浏览器请求,你将看到类似于图3的输出。


图3 process.jsp被加载

输入你的姓名和邮件地址,并点击Process提交表单进行处理,你将看到象图4这样的输出:



图4 表格被处理

可重用组件

上述示例表单不包含太多的代码,是很简单的。当更多的代码被包含时,则一个很重要的事情是不要在同一个文件中把业务逻辑和前端表现混淆在一起。把业务逻辑和表现两方面区分开能使得对任何一方面的改动不会影响到另一方面。然而,产生JSP代码应该主要局限于前端表现部分。这样,你如何实现业务逻辑部分呢?

这正是JavaBeans上场的地方。这种技术是一个可移植的、与平台无关的组件模型,能使编程人员编写组件,并在任何地方重用它们。在JSP的上下文之间,JavaBeans包含业务逻辑,并返回数据给JSP页面上的一个脚本,再由脚本把从JavaBeans组件返回的数据进行格式化,交给浏览器进行显示。一个JSP页面使用JavaBean组件主要是通过对其提供的属性进行设置和读取来进行操作的。

有什么好处

JSP页面中使用JavaBeans有很多好处:

• 可重用组件:不同的应用可以重复使用组件

• 业务逻辑和表现逻辑的分离:你可以改变数据显示的方式,而不影响业务逻辑

• 通过对源代码的保密,来保护你的知识产权

例子:在JSP中使用JavaBeans

现在让我们来看看如何修改process.jsp范例来使用JavaBeans。在上述表单中,有两个域:name和email。在JavaBeans中,这些被称为属性。所以,第一步你先写一个JavaBean组件,包含setX和getX方法,其中X是属性名字。例如,如果有一对读取和设置方法:setName和getName,那你就有一个名为name的属性。范例5给出一个FormBean组件。

优秀的组件必须能够与不同厂商提供的其它组件进行交互。所以,为了达到组件重用的目的,有两条重要规则(它是由JavaBeans架构规定的)要遵守:

1.你的Bean类必须提供一个不带参数的构造器(constructor),从而可以使用Beans.instantiate来创建它的实例。

2.你的Bean类必须支持持久性(persistence),即实现接口Serializable或Externalizable。

范例 5: FormBean.java

package userinfo;

import java.io.*;

public class FormBean implements Serializable {

private String name;

private String email;

public FormBean() {

name = null;

email = null;

}

public void setName(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setEmail(String email) {

this.email = email;

}

public String getEmail() {

return email;

}

}

为了在JSP文件中使用FormBean组件,你需要实例化这个Bean组件。这是通过使用<jsp:useBean>标记来实现的。下一行的<jsp:setProperty>在Bean被实例化之后执行,用于初始化Bean的属性。本例中的两个属性(name和email)被使用同一个语句设置。或者你也可以一次设置只一个属性,但是你首先需要读取表单中的数据,下面是一个如何设置name属性的例子:

<%! String yourname, youremail; %>

<% yourname =

request.getParameter("name"); %>

<jsp:setProperty name=

"formbean" property="name"

value="<%=yourname%>"/>

一旦使用从表单中读取的数据对属性进行了初始化,属性的值就可以使用<jsp:getProperty>进行提取并用于显示了。如范例6中else部分所示:

范例6: process2.jsp

<jsp:useBean id="formbean" class=

"userinfo.FormBean"/>

<jsp:setProperty name="formbean" property=

"*"/>

<HTML>

<HEAD>

<TITLE>Form Example</TITLE>

</HEAD>

<BODY BGCOLOR="#ffffcc">

<% if (request.getParameter("name")==null

&& request.getParameter("email") == null) { %>

<CENTER>

<H2>User Info Request Form </H2>

<form method="GET" action="process2.jsp">

<P>

Your name: <input type="text" name=

"name" size=27>

<p>

Your email: <input type="text" name=

"email" size=27>

<P>

<input type="submit" value="Process">

</FORM>

</CENTER>

<% } else { %>

<P>

<B>You have provided the following info</B>:

<P>

<B>Name</B>: <jsp:getProperty name=

"formbean"

property="name"/>

<P>

<B>Email</B>: <jsp:getProperty

name="formbean" property="email"/>

<% } %>

</BODY>

</HTML>

结论

开发人员如果有意于开发高质量的Web应用,他们应该熟悉那些不仅可以应用于今天的市场、而且还能够同样应用于明天的市场的技术,这些技术就是JSP和XML。下一篇文章将探讨JSP技术所提供的非常适合与XML配合使用的能力;并介绍如何有效地协同使用XML与JSP。JSP和XML为信息共享的Web应用提供了一个优秀的组合,因为JSP页面对XML的支持是直接用JSP个性化标记库的形式来实现的。在本系列中的下一篇文章中将介绍更多的信息。

第二部分:JSP与XML

作者:Qusay H. Mahmoud

可扩展标记语言(Extensible Markup Language, XML)已经成为事实上的互联网标准数据表示格式。XML数据可以在任何平台上被处理和解释-从手持设备到大型主机。它是那些需要可移植数据的Java应用的完美合作者。

Java是使用XML的众多编程语言中的胜利者。大多数XML解析器(parser)是用Java编写的,而且它有一套完整的Java API集合,专用于开发基于XML的应用。JavaServer Pages(JSP)技术能够访问所有这些API,因为它能够使用Java平台的所有功能、访问编程语言对象来解析并转换XML文件。JSP在设计之中就考虑着XML;你可以把一个JSP页面写成一个XML文档!

本文提供一个XML的简要概述,然后,介绍如何:

• 表现XML文档

• 使用JSP生成XML

• 使用Simple Access API for XML(SAX)和Document Object Model(DOM)来解析XML文档

• 在JSP中使用SAX和DOM

• 将XML转换成其它标记语言

XML概述

XML是一种元语言(metalanguage),用于定义包含结构化数据的文档。XML的特性和优势可以被概括成以下几个方面:

• 扩充性:作为一种元语言,XML可以被用于创建它自己的标记语言。今天有很多基于XML标记语言,例如无线标记语言(Wireless Markup Language, WML)。

• 精确的结构:HTML的痛苦在于其松散的结构,这使有效地处理HTML文档成为一件很困难的事情。与之相反,XML文档结构化很好,每一个文档有一个根元素(root element),而且所有的元素必须被嵌入在其它元素之中。

• 两种文档类型:有两种主要的XML文档类型

1.有效的文档(Valid document):一个有效的XML文档是由Document Type Definition(DTD)定义的。DTD是文档的语法,它定义了哪种类型的元素(element)、属性(attribute)和实体(entity)可以在文档中使用。DTD不仅定义了哪些元素可以或者必须出现,也定义了它们出现的顺序。

2.格式化的文档(Well-Formed document):一个格式化的文档不必遵从DTD。但是它必须遵循两个原则:1)每个元素必须有一个开始标记和一个结束标记。2)文档必须有一个根元素,它包含所有的其它元素。

• 强大的可扩展性:象我们前面提到的一样,XML只是用于定义语法。换句话说,它是用于定义内容。若想定义语义、风格和表现, 你需要使用可扩展风格语言(Extensible Stylesheet Language, XSL)。注意一个XML文档可能有多个XSL与之对应以供不同的用户使用。

注意:如果文档简单的话,使用一个格式化的文档(Well-Formed document)是便利的;或者当处理网络上的简单结构时,这时带宽是一个大问题,使用一个格式化的文档会更方便因为它无需额外传输复杂的DTD。

XML和HTML对比

XML和HTML都是标记语言,使用标记来注释数据。主要的区别在于:

• 在HTML中,文档的语法和语义都是定义好的,HTML自己就可以为用户创建一个可视化的表现。XML只允许你定义文档的语法。

• HTML文档的格式化不是很好。例如,不是所有的标记都有匹配的结束标记。XML是很好格式化的。

• 标记名称在XML中是区分大小写的,而HTML中不是。

让我们看一个XML文档。这个例子是一个简单的XML文档,它来自一个虚构的用于表示股票交易的报价服务器。象你所看到的,有一个根元素“portfolio”,而且,所有元素都有开始标记和结束标记。

范例1: stocks.xml

<?xml version="1.0" encoding="UTF-8"?>

<portfolio>

<stock>

<symbol>SUNW</symbol>

<name>Sun Microsystems</name>

<price>17.1</price>

</stock>

<stock>

<symbol>AOL</symbol>

<name>America Online</name>

<price>51.05</price>

</stock>

<stock>

<symbol>IBM</symbol>

<name>International Business Machines</name>

<price>116.10</price>

</stock>

<stock>

<symbol>MOT</symbol>

<name>MOTOROLA</name>

<price>15.20</price>

</stock>

</portfolio>

范例1的第一行说明XML版本号,是1.0;并让处理器知道,采用的编码规则是“UTF-8”。

尽管这是一个简单的文档,但因为它的结构化非常好,它包含了使它能够非常容易地被处理的有用信息。例如,股票服务器要根据股票价格,按某种顺序对交易进行排列。

显示XML文件

XML文档包含了可移植的数据。这说明范例1中的文档能够被处理并输出到不同的浏览器上(个人电脑的桌面浏览器,手持设备的微型浏览器)。换句话说,一个XML文档能够被转换成HTML或WML或任何其它标记语言。

如果你在一个支持XML的浏览器上(例如:微软的IE)加载stocks.xml,你将看到类似图1的内容。


图1:浏览器中的stocks.xml

基本上,浏览器已经解析了文档并用一种结构化的方式显示出来。但是,这些从用户的观点来看并不是十分有用的。用户希望看到的数据是以容易浏览的方式显示出来的有用信息。

一种显示XML文档的有效方式是对XML文档进行转换,要么提取数据,要么生成一个新的格式(例如把XML数据转换成HTML)。这种转换可以使用象Extensible Stylesheet Language Transformation(XSLT)这样的转换语言来实现,XSLT是XSL的一部分。XSL允许你为特定的格式化语义编写XML词汇表。换句话说,XSL有如下两部分,而且它本身是World Wide Web Consortium(W3C) Style Activity的一个组成部分:

• 转换语言(XSLT)

• 格式化语言(XSL格式化对象)

范例2中的XSL stylesheet为portfolio元素进行所需的转换。它生成HTML标记并从stocks.xml文档的元素中提取数据。

范例2: stocks.xsl

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl=

"http://www.w3.org/TR/WD-xsl">

<xsl:template match="/">

<html>

<head>

<title>Stocks</title>

</head>

<body bgcolor="#ffffcc" text="#0000ff">

<xsl:apply-templates/>

</body>

</html>

</xsl:template>

<xsl:template match="portfolio">

<table border="2" width="50%">

<tr>

<th>Stock Symbol</th>

<th>Company Name</th>

<th>Price</th>

</tr>

<xsl:for-each select="stock">

<tr>

<td>

<i><xsl:value-of select=

"symbol"/></i>

</td>

<td>

<xsl:value-of select="name"/>

</td>

<td>

<xsl:value-of select="price"/>

</td>

</tr>

</xsl:for-each>

</table>

</xsl:template>

</xsl:stylesheet>



看上去有一些复杂,是吧?一开始时是这样,一旦你清楚了XSL的语法,会感觉它是非常直观的。这是以上语法的含义:

• xsl:stylesheet:根元素

• xsl:template:如何转换选定的节点

• match:一个用于选择节点的属性

• “/”: 输入的XML文档的根节点

• xsl:apply-templates:对所选定的节点的子节点使用模板

• xsl:value-of:选定节点(从所选定的节点中提取数据)

现在的问题是如何与stocks.xml文档一起使用这个XSL stylesheet?答案很简单:我们需要修改范例1中的stocks.xml文件的第一行,使它按照stocks.xsl进行显示。现在,范例1的第一行应该是:

<?xml:stylesheet type="text/xsl" href="stocks.xsl" version="1.0" encoding="UTF-8"?>

这句话的的意思基本是说,当你在一个浏览器中加载stocks.xml时(它将作为节点树被进行解析),相应的stylesheet应该被用于从节点树上提取数据。当你在一个支持XML和XSL的浏览器(例如:微软的IE)中加载这个修改过的stocks.xml时,你将看到类似图2的内容:




图2:使用stylesheet(stocks.xsl)

从JSP中生成XML

JSP技术可以用于生成XML文档。一个JSP页面能够很容易地生成一个响应并在其中包含范例1中的stocks.xml文档,如范例3所示。生成XML的主要需求是JSP页面必须正确地设置页面的内容类别(content type)。象你从范例3中看到的一样,内容类别被设置成text/xml。这种技术同样可以被用于生成其它标记语言(如WML)。

范例3: genXML.jsp

<%@ page contentType="text/xml" %>

<?xml version="1.0" encoding="UTF-8"?>

<portfolio>

<stock>

<symbol>SUNW</symbol>

<name>Sun Microsystems</name>

<price>17.1</price>

</stock>

<stock>

<symbol>AOL</symbol>

<name>America Online</name>

<price>51.05</price>

</stock>

<stock>

<symbol>IBM</symbol>

<name>International Business

Machines</name>

<price>116.10</price>

</stock>

<stock>

<symbol>MOT</symbol>

<name>MOTOROLA</name>

<price>15.20</price>

</stock>

</portfolio>

如果你在一个Web服务器(例如:Tomcat)上调用genXML.jsp页面,你将看到与图1类似的内容。

使用JSP和JavaBeans生成XML

用于生成XML的数据也可以从JavaBeans组件中提取。例如,下面的JavaBeans组件,PortfolioBean,定义了一个含有股票数据的Bean。

范例4: PortfolioBean.java

package stocks;

import java.util.*;

public class PortfolioBean implements

java.io.Serializable {

private Vector portfolio = new Vector();

public PortfolioBean() {

portfolio.addElement(new Stock("SUNW",

"Sun Microsystems", (float) 17.1));

portfolio.addElement(new Stock("AOL",

"America Online", (float) 51.05));

portfolio.addElement(new Stock("IBM",

"International Business Machines",

(float) 116.10));

portfolio.addElement(new Stock("MOT",

"MOTOROLA", (float) 15.20));

}

public Iterator getPortfolio() {

return portfolio.iterator();

}

}

PortfolioBean类中使用的Stock类,如范例5所示:

范例5: Stock.java

package stocks;

public class Stock implements java.io.Serializable {

private String symbol;

private String name;

private float price;

public Stock(String symbol, String name,

float price) {

this.symbol = symbol;

this.name = name;

this.price = price;

}

public String getSymbol() {

return symbol;

}

public String getName() {

return name;

}

public float getPrice() {

return price;

}

}

现在,我们可以编写一个JSP页面来生成一个XML文档,且它的数据是从PortfolioBean中提取的,如范例6所示:

范例6: stocks.jsp

<%@ page contentType="text/xml" %>

<%@ page import="stocks.*" %>

<jsp:useBean id="portfolio"

class="stocks.PortfolioBean" />

<%

java.util.Iterator folio =

portfolio.getPortfolio();

Stock stock = null;

%>

<?xml version="1.0" encoding="UTF-8"?>

<portfolio>

<% while (folio.hasNext()) { %>

<% stock = (Stock)folio.next(); %>

<stock>

<symbol><%=

stock.getSymbol() %></symbol>

<name><%=

stock.getName() %></name>

<price><%=

stock.getPrice() %></price>

</stock>

<% } %>

</portfolio>

如果你从一个支持XML的浏览器中,调用stocks.jsp页面,你将看到类似图3的内容。




图3:使用JSP和JavaBeans生成的XML

如果你把:

<?xml version="1.0" encoding="UTF-8"?>

替换成使用指定的stylesheet:

<?xml:stylesheet type="text/xsl" href="stocks.xsl" version="1.0" encoding="UTF-8"?>

XML文档将被生成,并且XSL stylesheet将被使用。你将得到如上述图2所示内容。

转换XML成为服务器端对象

我们已经了解了如何生成XML,现在的问题是如何在应用中使用它。为了做到这一点,我们需要转换XML成为服务器端对象,并提取对象属性。转换不是自动的;我们必须手工解析一个XML文档,并把它封装到一个JavaBeans组件中。但是在将来,XML/Java绑定技术(XML/Java Binding Technology)将能够自动化这一过程,它将能够使你编译一个XML Schema并生成对应的Java类。

为了解析,两组接口可以使用:

• Simple API for XML(SAX)

• Document Object Model(DOM)

在我们探讨这两种解析技术之前,我们先描述一下软件环境。

软件环境

我们将用于解析的软件环境是Java API for XML Processing(JAXP) 1.1版,它支持SAX 2.0, DOM level 2和XSL转换。JAXP可以从http://java.sun.com/xml/download.html下载。

安装,在你选择的目录中unzip它,并修改classpath使其包含JAXP的三个JAR文件的路径:

• crimson.jar:缺省的XML解析器,是Sun公司的Java Project X解析器的衍生

• xalan.jar:缺省的XSLT引擎

• jaxp.jar:API

或者你可以把这些JAR文件作为Java 2平台的扩展进行安装,则只需要简单地复制这些文件到JAVA_HOME/jre/lib/ext目录下,这里JAVA_HOME是你安装JDK的目录(例如 c:\jdk1.3)。把JAR文件作为Java 2的扩展进行安装时,无须修改classpath。

Simple API for XML(SAX)

SAX是一组用于处理XML的简单API。它不是一个解析器!它只是一个标准接口,可以由很多不同的XML解析器来实现,它是由XML-DEV邮件组(http://www.lists.ic.ac.uk/hypermail/xml-dev/)的成员们开发的,现在由OASIS(http://www.oasis-open.org/)主持。

SAX的主要优势是:它是轻量级的和快速的。这主要是因为它是基于事件驱动的API,意思是它使用回滚(callback)直接向应用报告解析事件(例如:元素的开始和终止),如图4所示。因此,应用需要实现自己的处理器(handler)来处理不同的事件,就象在图形用户界面中处理事件一样。


图4:SAX使用回滚将相应的事件通知处理器

使用SAX,如范例7所示,包含以下步骤:

• 实现一个或多个事件处理器(在这个例子中实现了ContentHandler)

• 创建一个XMLReader

• 创建一个InputSource

• 对input source调用parse方法

注意:MySAXParseBean覆盖了方法startElement,endElement和characters,所有这些是ContentHandler接口定义的方法。解析器会在XML文档中的每一个元素开始的地方调用startElement方法,然后调用characters方法报告每一块的字符数据,而最后,在XML文档中的每一个元素结束的时候,调用endElement方法。

范例7:MyParserBean.java

package saxbean;

import java.io.*;

import java.util.*;

import org.xml.sax.*;

import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParserFactory;

import

javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

public class MySAXParserBean extends

DefaultHandler implements java.io.Serializable {

private String text;

private Vector vector = new Vector();

private MyElement current = null;

public MySAXParserBean() {

}

public Vector parse(String filename) throws

Exception {

SAXParserFactory spf =

SAXParserFactory.newInstance();

spf.setValidating(false);

SAXParser saxParser = spf.newSAXParser();

// create an XML reader

XMLReader reader = saxParser.getXMLReader();

FileReader file = new FileReader(filename);

// set handler

reader.setContentHandler(this);

// call parse on an input source

reader.parse(new InputSource(file));

return vector;

}

// receive notification of the beginning of an element

public void startElement (String uri, String name,

String qName, Attributes atts) {

current = new MyElement(

uri, name, qName, atts);

vector.addElement(current);

text = new String();

}

// receive notification of the end of an element

public void endElement (String uri, String name,

String qName) {

if(current != null && text != null) {

current.setValue(text.trim());

}

current = null;

}

// receive notification of character data

public void characters (char ch[], int start,

int length) {

if(current != null && text != null) {

String value = new String(

ch, start, length);

text += value;

}

}

}

MySAXParserBean使用了MyElement类,在范例8中定义的。

范例8:MyElement.java

package saxbean;

import org.xml.sax.Attributes;

public class MyElement implements

java.io.Serializable {

String uri;

String localName;

String qName;

String value=null;

Attributes attributes;

public MyElement(String uri, String localName,

String qName, Attributes attributes) {

this.uri = uri;

this.localName = localName;

this.qName = qName;

this.attributes = attributes;

}

public String getUri() {

return uri;

}

public String getLocalName() {

return localName;

}

public String getQname() {

return qName;

}

public Attributes getAttributes() {

return attributes;

}

public String getValue() {

return value;

}

public void setValue(String value) {

this.value = value;

}

}

现在,如果你喜欢,你可以通过命令行来测试MySAXParserBean,以确认它是可用的。范例9是一个简单的测试程序。

范例9:MyTestDriver.java

import java.io.*;

import java.util.*;

public class MyTestDriver {

public static void main(String argv[]) {

String file = new String(argv[0]);

MySAXParserBean p = new MySAXParserBean();

String str = null;

try {

Collection v = p.parse(file);

Iterator it = v.iterator();

while(it.hasNext()) {

MyElement element =

(MyElement)it.next();

String tag = element.getLocalName();

if(tag.equals("symbol")) {

System.out.println("Symbol:

" + element.getValue());

} else if(tag.equals("name")) {

System.out.println("Name: "

+element.getValue());

} else if (tag.equals("price")) {

System.out.println("Price: "

+element.getValue());

}

}

} catch (Exception e) {

}

}

}

如果你运行MyTestDriver,并使用范例1的stocks.xml作为XML输入文件。你将看到类似图5的内容。


图5:通过命令行测试XML解析器

现在,让我们看看如何在JSP页面中使用MySAXParserBean。象你可以从范例10中看到的,这是非常直观的。我们为XML文件(stocks.xml)调用MySAXParserBean的parse方法,然后重复从stocks中抽取数据,并格式化成HTML表格。

注意:输入到解析方法中的XML文件可以是一个指向XML文件的URL。例如:

http://www.host.com/xml/ebiz.xml。

范例10:saxstocks.jsp

<html>

<head>

<title>sax parser</title>

<%@ page import="java.util.*" %>

<%@ page import="saxbean.*" %>

</head>

<body bgcolor="#ffffcc">

<jsp:useBean id="saxparser"

class="saxbean.MySAXParserBean" />

<%

Collection stocks =

saxparser.parse("c:/stocks/stocks.xml");

Iterator ir = stocks.iterator();

%>

<center>

<h3>My Portfolio</h3>

<table border="2" width="50%">

<tr>

<th>Stock Symbol</th>

<th>Company Name</th>

<th>Price</th>

</tr>

<tr>

<%

while(ir.hasNext()) {

MyElement element = (MyElement) ir.next();

String tag = element.getLocalName();

if(tag.equals("symbol")) { %>

<td><%= element.getValue() %></td>

<% } else if (tag.equals("name")) { %>

<td><%= element.getValue() %></td>

<% } else if (tag.equals("price")) { %>

<td><%= element.getValue() %><

/td></tr><tr>

<% } %>

<% } %>

</table>

</body>

</html>

如果你调用saxstocks.jsp,你将看到类似图6的内容。


图6:通过JSP页面使用MySAXParserBean

Document Object Model(DOM)

DOM是一个对平台和语言中立的接口,用于访问和更新XML文档。与SAX不同,DOM访问XML文档是通过一个由元素节点和文本节点组成的树形结构,如图7所示。


图7:DOM为XML文档创建一个树

由于树将在内存中创建,因此使用DOM需要很大的内存。但DOM的好处是,比SAX编程简单。象你可以从范例11中看到的,一个XML文档可以通过三行代码就转换成树。一旦你有了树,就可以反复遍历和操作它。

范例11:MyDOMParserBean.java

package dombean;

import javax.xml.parsers.*;

import org.w3c.dom.*;

import java.io.*;

public class MyDOMParserBean

implements java.io.Serializable {

public MyDOMParserBean() {

}

public static Document

getDocument(String file) throws Exception {

// Step 1: create a DocumentBuilderFactory

DocumentBuilderFactory dbf =

DocumentBuilderFactory.newInstance();

// Step 2: create a DocumentBuilder

DocumentBuilder db = dbf.newDocumentBuilder();

// Step 3: parse the input file to get a Document object

Document doc = db.parse(new File(file));

return doc;

}

}

这将生成一棵树,而我们需要遍历它。这是通过使用范例12中的JSP页面domstocks.jsp中的traverseTree方法实现的。

范例12:domstocks.jsp

<html>

<head>

<title>dom parser</title>

<%@ page import="javax.xml.parsers.*" %>

<%@ page import="org.w3c.dom.*" %>

<%@ page import="dombean.*" %>

</head>

<body bgcolor="#ffffcc">

<center>

<h3>My Portfolio</h3>

<table border="2" width="50%">

<tr>

<th>Stock Symbol</th>

<th>Company Name</th>

<th>Price</th>

</tr>

<jsp:useBean id="domparser"

class="dombean.MyDOMParserBean" />

<%

Document doc =

domparser.getDocument("c:/stocks/stocks.xml");

traverseTree(doc, out);

%>

<%! private void traverseTree(Node node,

JspWriter out) throws Exception {

if(node == null) {

return;

}

int type = node.getNodeType();

switch (type) {

// handle document nodes

case Node.DOCUMENT_NODE: {

out.println("<tr>");

traverseTree

(((Document)node).getDocumentElement(),

out);

break;

}

// handle element nodes

case Node.ELEMENT_NODE: {

String elementName = node.getNodeName();

if(elementName.equals("stock")) {

out.println("</tr><tr>");

}

NodeList childNodes =

node.getChildNodes();

if(childNodes != null) {

int length = childNodes.getLength();

for (int loopIndex = 0; loopIndex <

length ; loopIndex++)

{

traverseTree

(childNodes.item(loopIndex),out);

}

}

break;

}

// handle text nodes

case Node.TEXT_NODE: {

String data =

node.getNodeValue().trim();

if((data.indexOf("\n") <0

) && (data.length()

> 0)) {

out.println("<td>"+

data+"</td>");

}

}

}

}

%>

</table>

</body>

</html>

如果你从浏览器中调用domstocks.jsp,你将看到与上述图6相似的内容。

转换XML

随着在更多的设备上可以使用浏览器,将互联网数据转换成多种格式,例如:XML、HTML、WML、或XHTML等,变得越来越重要。XSLT可以用于把XML文件转换成任何想要的格式。转换引擎或处理器将以XML文档作为输入,并用XSL stylesheet创建新的文件格式,如图8所示。


图8:使用XSLT引擎

下述范例,xml2html,展示如何把XML文件stocks.xml 转换成一个HTML文件stocks.html。象你看到的一样,一旦你有了XML文件和要使用的XSL stylesheet文件后,整个转换将在三行代码之内完成。

范例13:xml2html.java

import javax.xml.transform.*;

import javax.xml.transform.stream.*;

import java.io.*;

public class xml2html {

public static void main(String[] args)

throws TransformerException,

TransformerConfigurationException,

FileNotFoundException, IOException

{

TransformerFactory tFactory =

TransformerFactory.newInstance();

Transformer transformer =

tFactory.newTransformer(

new StreamSource("stocks.xsl"));

transformer.transform(

new StreamSource(args[0]),

new StreamResult(new FileOutputStream(

args[1])));

System.out.println("** The output is written in "+

args[1]+" **");

}

}

使用以下命令来执行这个范例:

c:>java xml2html stocks.xml stocks.html

stocks.xml是输入,它将被根据stylesheet文件stocks.xsl转换成stocks.html。这里我们使用了先前产生的stylesheet,但是我们增加了几行以指定输出方法(本例中是HTML),如范例14所示。

范例14:stocks.xsl

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl=

"http://www.w3.org/1999/XSL/Transform" version=

"1.0">

<xsl:output method="html" indent="yes"/>

<xsl:template match="/">

<html>

<body>

<xsl:apply-templates/>

</body>

</html>

</xsl:template>

<xsl:template match="portfolio">

<table border="2" width="50%">

<xsl:for-each select="stock">

<tr>

<td>

<i><xsl:value-of select=

"symbol"/></i>

</td>

<td>

<xsl:value-of select="name"/>

</td>

<td>

<xsl:value-of select="price"/>

</td>

</tr>

</xsl:for-each>

</table>

</xsl:template>

</xsl:stylesheet>

HTML文件将被生成,如图9所示:


图9:xml2html转换

结论

在本系列的前一篇文章“使用JSP和XML开发Web应用,第一部分:JSP 快揽”中,对于JSP是否允许开发人员把业务逻辑和表现分开,展开了热烈讨论。我的答案是要看你如何使用JSP!本文中我们已经看到如何使用不同的解析技术生成相同的表现。但在有些范例中,业务逻辑和表现是混合在一起的。在下一篇文章中,我们将向你展示如何编写你自己的个性化标记和标记库,以帮助你分割业务逻辑和表现。

第三部分:开发JSP个性化标记

作者:Qusay H. Mahmoud

JSP技术使在HTML文件中嵌入小段的Java代码成为很容易的事情。然而这种解决方案并不是适合所有的HTML内容开发人员,可能是因为它们不熟悉Java并且不想学习Java的语法。尽管JavaBeans能够封装很多Java代码,但是在JSP页面中使用它们仍然需要内容开发人员了解相当的Java语法知识。

JSP技术允许你通过标记库机制,引入新的个性化标记。作为一个Java开发人员,你可以通过引入个性化标记来扩展JSP页面,而且它们是使用类似HTML的语法进行部署和使用的。个性化标记也允许你提供更好的封装,提高业务逻辑和表现逻辑的分割。

本文概要介绍了个性化标记,并介绍:

如何开发并部署简单的标记

如何开发并部署高级标记:带有参数的标记和含有主体的标记

如何使用标记库描述符(Tag Library Descriptor, TLD)来描述标记

最后,提供一些编程指南。

标记概述

如果你对HTML有经验,你已经知道了能够使用的标记的类型。基本上,有两类标记,都可以有属性(attribute:告诉标记应如何完成它们的工作的信息)。

无主体标记(Bodyless Tags):一个无主体标记是一个含有开始标记但是没有相应的结束标记的标记。他的语法是:

<tagName attributeName="value"

anotherAttributeName="anotherValue"/>

无主体标记被用于表示特定的功能,如表示一个输入域,或显示一个图像。这里是一个HTML中的无主体标记的例子:

<IMG SRC="developer/technicalArticles/xml/WebAppDev3/fig10.gif">

含有主体的标记:一个含有开始标记和与之匹配的结束标记的标记。它的语法是:

<tagName attributeName="value"

anotherAttributeName="anotherValue">

...tag body...

</tagName>

带有主体的标记被用于执行主体内容中的操作,例如格式化。这里是HTML中的一个带有主体的标记的例子:

<H2>Custom Tags</H2>

JSP个性化标记(Custom Tags)

JSP个性化标记仅仅是实现了特定接口的Java类。一旦它们被开发并部署了,你可以在HTML中使用XML语法调用它们的操作。它们有开始标记和结束标记。它们可能带有或者不带有主体。一个无主体标记可以被表示为:

<tagLibrary:tagName />

而一个带有主体的标记可以被表示为:

<tagLibrary:tagName>

body

</tagLibrary:tagName>

同样,两种类型的标记可能带有用于确定标记的行为的属性。下述标记带有一个被称为name的属性,它接收一个String类型的值(此处是对变量yourName求值):

<mylib:hello name="<%=yourName %>" />

或者,它可以被写成带有主体的标记:

<mylib:hello>

<%= yourName %>

</mylib:hello>

注意:任何简单的字符串类型的数据,或者是可以通过对一个简单表达式求值所得到的数据,都应该通过属性传递,而不要作为主体的内容。

个性化标记的益处

对于JSP个性化标记必须注意的非常重要的一点是它并不比脚本代码(scriptlet)提供更多的功能,它们仅仅是提供更好的包装,以帮助你改进业务逻辑和表现逻辑的分割。个性化标记的一些益处如下:

它们可以减少或消除你的JSP应用中的scriptlet。任何需要传递到标记的参数,可以以属性的形式或主体内容的形式传递,而不需要任何Java代码来初始化或设置组件属性。

语法更加简单。scriptlet用Java编写,但是个性化标记使用类似HTML的语法。

它们能够改善那些作为非编程人员的内容开发者的生产效率,允许它们执行HTML做不了的工作。

它们是可重用的。它们节约开发和测试时间。scriptlet是不可重用的,除非你把剪贴(cut-and-paste)叫做可重用。

简单地讲,你可以象使用HTML创建一个表现页面一样,使用个性化标记完成复杂的任务。

定义一个标记

一个标记是一个实现了一个特定接口的Java类。它被用于从JSP页面中封装功能。象我们前面提到的,一个标记可以是无主体的或有主体的。为了定义一个简单的无主体标记,你的类必须实现Tag接口。开发有主体的标记将在后面讨论。范例1展示了你必须实现的标记接口的源代码。

范例1:Tag.java

public interface Tag {

public final static int SKIP_BODY = 0;

public final static int EVAL_BODY_INCLUDE = 1;

public final static int SKIP_PAGE = 5;

public final static int EVAL_PAGE = 6;

void setPageContext(PageContext pageContext);

void setParent(Tag parent);

Tag getParent();

int doStartTag() throws JspException;

int doEndTag() throws JspException;

void release();

}

所有的标记都必须实现Tag接口(或者是它的一个子接口),因为它定义了JSP运行时引擎调用并执行一个标记所需要的所有方法。表1提供了标记接口中的方法的描述。

表1:Tag接口中的方法的描述

方法
描述

setPageContext(PageContext pc)
这个方法被JSP运行时环境调用,在doStartTag之前,用于设置页面的上下文。

setParent(Tag parent)
被JSP运行时环境调用,在doStartTag之前,用于向标记处理器传递一个对它的父标记的引用(reference)。

getParent
返回一个Tag类的实例:是这个标记的父标记。

doStartTag
被JSP运行时环境调用,用于提示标记处理器为这个实例处理开始标记。

doEndTag
被JSP运行时环境在从doStartTag返回后调用。标记操作的主体可能被、也可能没有被处理,这取决于doStartTag返回的值。

release
被JSP运行时环境调用,指示标记处理器执行任何必须的清除工作。


我的第一个标记

现在,让我们看一个示范标记,它在被调用时,向客户端打印一条消息。

在开发一个个性化标记时包含一些步骤,可以归纳为:

开发标记处理器

创建一个标记库描述符

测试这个标记

开发标记处理器

标记处理器(Tag Handler)是一个类,被JSP运行时环境调用,在引用这个标记的JSP页面的执行过程中审核这个个性化标记。标记处理器的方法在标记审核过程之中不同的地方被实现类调用。每一个标记处理器必须实现一个特殊的接口。在这个例子中,一个简单的标记实现了Tag接口,如范例2所示。

范例2:HelloTag.java

package tags;

import java.io.*;

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

public class HelloTag implements Tag {

private PageContext pageContext;

private Tag parent;

public HelloTag() {

super();

}

public int doStartTag() throws JspException {

try {

pageContext.getOut().print(

"This is my first tag!");

} catch (IOException ioe) {

throw new JspException("Error:

IOException while writing to client"

+ ioe.getMessage());

}

return SKIP_BODY;

}

public int doEndTag() throws JspException {

return SKIP_PAGE;

}

public void release() {

}

public void setPageContext(PageContext

pageContext) {

this.pageContext = pageContext;

}

public void setParent(Tag parent) {

this.parent = parent;

}

public Tag getParent() {

return parent;

}

}

在HelloTag中有两个重要的方法要注意,doStartTag和doEndTag。doStartTag方法是在遇到开始标记时被调用的。在这个例子中,这个方法返回SKIP_BODY,因为本例的简单标记是没有主体的。doEndTag方法是在遇到结束标记时被调用的。在这个例子中,这个方法返回SKIP_PAGE,因为我们不想审核页面的其它部分,否则它应该返回EVAL_PAGE。

为了编译HelloTag类,假设Tomcat被安装在 c:\tomcat:

创建一个新的子目录,称为tags,这是HelloTag类所在的包的名称。它应该被创建在:

c:\tomcat\webapps\examples\web-inf\classes。

将HelloTag.java存放到tags子目录中。

使用如下命令编译:

c:\tomcat\webapps\examples\web-inf\classes\tags>

javac -classpath c:\tomcat\lib\servlet.jar HelloTag.java

创建标记库描述符

下一步是指定这个标记将如何被JSP运行时环境在执行时使用。这是通过创建一个标记库描述符(Tag Library Descriptor, TLD)来实现的,它是一个XML文件。范例3展示了一个样本TLD。

范例3:mytaglib.tld

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE taglib

PUBLIC "-//Sun Microsystems, Inc.//

DTD JSP Tag Library 1.1//EN"

"http://java.sun.com/j2ee/dtds/

web-jsptaglibrary_1_1.dtd">

<!-- a tag library descriptor -->

<taglib>

<tlibversion>1.0</tlibversion>

<jspversion>1.1</jspversion>

<shortname>first</shortname>

<uri></uri>

<info>A simple tab library for the

examples</info>

<tag>

<name>hello</name>

<tagclass>tags.HelloTag</tagclass>

<bodycontent>empty</bodycontent>

<info>Say Hi</info>

</tag>

</taglib>

首先,我们指定标记库版本和JSP版本。<shortname>标记指明我们将如何从JSP页面中引用标记库。<uri>标记被用作你的标记库的唯一标示符。

在这个TLD中,我们只有一个标记,名为hello,使用<tagclass>标记指定它对应的类。但是,一个标记库可以根据你的需要,有任意多个标记。<bodycontent>告诉我们这个标记将没有主体;否则将产生错误信息。另一方面,如果你想审核标记的主体,那么此处的值将是:

tagdependent:意思是标记的任何主体都将由标记自己控制,而且它可以为空。

JSP:意思是JSP容器将审核所有标记的主体,但是它也可以为空。

存放mytaglib.tld的目录是:c:\tomcat\webapps\examples\web-inf\jsp。

3. 测试这个标记

最后一步是测试我们开发的标记。为了使用这个标记,我们需要引用它,有三种方式可以引用标记:

引用一个未打包的标记库的标记库描述符,例如:

<@ taglib uri="/WEB-INF/jsp/mytaglib.tld"

prefix="first" %>

引用包含标记库的JAR文件,例如:

<@ taglib uri="/WEB-INF/myJARfile.jar"

prefix='first" %>

在web应用描述符中定义一个对标记库描述符的引用,并为从JSP中引用标记库定义一个短名字。实现这些,打开文件:c:\tomcat\webapps\web-inf\web.xml,并在结束行(</web-app>)前增加以下各行:

<taglib>

<taglib-uri>mytags</taglib-uri>

<taglib-location>/WEB-INF/jsp/

mytaglib.tld</taglib-location>

</taglib>

现在,编写一个JSP并使用第一种语法:

范例4:Hello.jsp

<%@ taglib uri="/WEB-INF/jsp/mytaglib.tld"

prefix="first" %>

<HTML>

<HEAD>

<TITLE>Hello Tag</TITLE>

</HEAD>

<BODY bgcolor="#ffffcc">

<B>My first tag prints</B>:

<first:hello/>

</BODY>

</HTML>

taglib是用于告诉JSP运行时环境,从什么地方找到我们的标记库的描述符,prefix指明我们将如何在这个库中引用标记。有了这些,JSP运行时环境能够在整个的JSP中,识别我们的标记的使用。只需要我们以前缀first开始我们的标记名,象在<first:hello/>中一样。

另外,你可以选用第二种引用方式,即创建一个JAR文件。或者,如果你用第三种引用方式,只需简单地用下述代码替换掉范例4中的第一行:

<%@ taglib uri="mytags" prefix="first" %>

基本上,我们已经使用mytags这个名字,它被加入到web.xml中,用于引用标记库。在本文的其它范例中,将使用这个引用。

现在,如果你通过浏览器请求Hello.jsp,你将见到如图1所示的内容。

图1:第一个个性化标记

范例4中开发的个性化标记是一个简单的标记,它的目的仅仅是对你在开发个性化标记时付出的努力给与一点回报。你可能已经注意到即使这样简单的一个标记,也需要我们实现一系列的方法,其中一些是很简单的实现。为了使付出的努力最小,JSP设计人员提供了一个模板,用于实现简单的标记。模板是TagSupport抽象类。它是一个很方便的类,它提供了Tag接口所定义的所有方法的缺省实现。

所以,一个编写简单标记的捷径是继承TagSupport类,而不是实现Tag接口。你可以把TagSupport抽象类看作一个适配器。这么说,范例4中的HelloTag类可以很容易的写为范例5所示。

范例5:扩充TagSupport类

package tags;

import java.io.*;

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

public class HelloTag extends TagSupport {

public int doStartTag() throws JspException {

try {

pageContext.getOut().print("This is my

first tag!");

} catch (IOException ioe) {

throw new JspException("Error:

IOException while writing

to client" + ioe.getMessage());

}

return SKIP_BODY;

}

public int doEndTag() throws JspException {

return SKIP_PAGE;

}

}

带有参数的标记

我们已经知道了如何开发简单标记。现在,让我们看看如何开发带有参数的标记--带有属性的标记。两个新的东西需要被增加到前面的例子中,以处理属性。

1.增加一个set方法

2.为mytagslib.tld增加一个新标记

在范例5中增加一个set方法并修改输出信息。

范例5:带有属性的标记

package tags;

import java.io.*;

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

public class HelloTagParam extends TagSupport {

private String name;

public void setName(String name) {

this.name = name;

}

public int doStartTag() throws JspException {

try {

pageContext.getOut().print("Welcome to

JSP Tag Programming, " +name);

} catch (IOException ioe) {

throw new JspException("Error:

IOException

while writing to client");

}

return SKIP_BODY;

}

public int doEndTag() throws JspException {

return SKIP_PAGE;

}

}

我们所要做的下一个工作是为mytagslib.tld增加一个新的标记,这个新标记如范例6所示。这一小段代码应该被添加在mytagslib.tld文件最后一行(</taglib>)之前:

范例6:修改mytaglib.tld

<tag>

<name>helloparam</name>

<tagclass>tags.HelloTagParam</tagclass>

<bodycontent>empty</bodycontent>

<info>Tag with Parameter</info>

<attribute>

<name>name</name>

<required>false</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

</tag>

我们已经增加了一个名为helloparam的标记。注意:新的<attribute>标记,它指定helloparam标记接收一个名为name的属性。<required>标记被设置为false,意思是这个属性是可选项;<rtexprvalue>标记被设置为false,指明运行时环境不做审核。

web.xml--web应用描述符无须添加任何内容,因为我们使用了相同的标记库mytaglib.tld。

现在,我们可以测试新的标记。范例7的源代码展示如何使用name属性"JavaDuke"来测试它。

范例7:HelloTagParam.jsp

<%@ taglib uri="mytags" prefix="first" %>

<HTML>

<HEAD>

<TITLE>Hello Tag with Parameter</TITLE>

</HEAD>

<BODY bgcolor="#ffffcc">

<B>My parameterized tag prints</B>:

<P>

<first:helloparam name="JavaDuke"/>

</BODY>

</HTML>

如果你从浏览器中调用HelloTagParam.jsp,你将看到与图2类似的内容。

图2:测试一个带有参数的标记

标记库

标记库是一组JSP个性化标记。Jakarta Taglibs Project提供了几种非常有用的标记库,用于XML解析、转换、邮件、数据库和其它用途。你可以很容易的下载使用。

这里我们开发我们自己的标记库。作为示例,我们开发一个简单的算术标记库,包含两个标记,一个用来进行两个数相加,另一个是一个数减去另一个数。每一个标记用一个类来表示。两个类的源代码,add.java和substract.java如范例8所示。

范例8:add.java和substract.java

package tags;

import java.io.*;

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

public class Add extends TagSupport {

private int num1, num2;

public void setNum1(int num1) {

this.num1 = num1;

}

public void setNum2(int num2) {

this.num2 = num2;

}

public int doStartTag() throws JspException {

try {

pageContext.getOut().print("Welcome

to First

Grade Math! ");

pageContext.getOut().print("The sum of: " +

num1 + " and " + num2 + " is: " + (

num1+num2));

} catch (IOException ioe) {

throw new JspException("Error:

IOException

while writing to client");

}

return SKIP_BODY;

}

}

// Subtract.java

package tags;

import java.io.*;

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

public class Subtract extends TagSupport {

private int num1, num2;

public void setNum1(int num1) {

this.num1 = num1;

}

public void setNum2(int num2) {

this.num2 = num2;

}

public int doStartTag() throws JspException {

try {

pageContext.getOut().print("Welcome to First

Grade Math! ");

pageContext.getOut().print("If you

subtract:

" + num2 + " from " + num1 +

", you get: "+ (num1 - num2));

} catch (IOException ioe) {

throw new JspException("Error:

IOException

while writing to client");

}

return SKIP_BODY;

}

}

源代码很容易理解。注意一个我们在add.java和substract.java中重复的问题是调用pageContext.getOut.print。更好的方式应该是获取一个JspWriter对象,然后通过它来打印:

JspWriter out = pageContext.getOut();

out.print("first line");

out.print("second line");

下一步是修改标记库描述符文件,mytaglib.tld,并增加对两个新标记的描述。范例9展示两个新标记的描述。在mytaglib.tld最后一行的前面增加下面XML代码段。

范例9:修改mytaglib.tld

<tag>

<name>add</name>

<tagclass>tags.Add</tagclass>

<bodycontent>empty</bodycontent>

<info>Tag with Parameter</info>

<attribute>

<name>num1</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

<attribute>

<name>num2</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

</tag>

<tag>

<name>sub</name>

<tagclass>tags.Subtract</tagclass>

<bodycontent>empty</bodycontent>

<info>Tag with Parameter</info>

<attribute>

<name>num1</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

<attribute>

<name>num2</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

</tag>

象你看到的一样,每一个标记需要两个属性,必须被命名为num1和num2。

现在,我们可以使用范例10中提供的测试程序来测试算术标记库。

范例10:math.jsp

<%@ taglib uri="mytags" prefix="math" %>

<HTML>

<HEAD>

<TITLE>Hello Tag with Parameter</TITLE>

</HEAD>

<BODY bgcolor="#ffffcc">

<B>Calling first tag</B>

<P>

<math:add num1="1212" num2="121"/>

<P>

<B>Calling second tag</B>

<P>

<math:sub num1="2123" num2="3"/>

</BODY>

</HTML>

如果你从一个浏览器中调用math.jsp,你将看到与图3类似的内容。

图3:测试算术标记库

带有主体的标记

带有主体的标记的标记处理器实现起来是不同的,它是根据主体需要审核一次还是多次来决定的。

单次审核:如果主体需要被审核一次,标记处理器应该实现Tag接口,或继承TagSupport抽象类;doStartTag方法需要返回EVAL_BODY_INCLUDE,而如果它根本不需要审核,则返回BODY_SKIP。

多次审核:如果主体需要被审核多次,BodyTag接口应该被实现。BodyTag接口继承于Tag接口并定义了额外的方法(setBodyContect,doInitBody,和doAfterBody)使标记处理器能够检察、甚至可能修改它的主体。另一种方式,类似于TagSupport类,你可以继承BodyTagSu




flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923