﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-优雅天平-文章分类-J2EE相关</title><link>http://www.blogjava.net/Victor/category/5620.html</link><description>享受喧嚣 安于平静</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:22:42 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:22:42 GMT</pubDate><ttl>60</ttl><item><title>XML alphabet soup</title><link>http://www.blogjava.net/Victor/articles/27524.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 11 Jan 2006 02:07:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/27524.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/27524.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/27524.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/27524.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/27524.html</trackback:ping><description><![CDATA[<P>[文章信息] <BR>&nbsp;<BR>作者: kissjava.com转 <BR>时间: 2005-05-12 12:31:15 <BR>出处: KissJava.com <BR>责任编辑: Icy <BR><BR>[文章导读] <BR>&nbsp;<BR>目前访问XML文档的API分成两种访问方式：顺序的和任意的访问，分别是：SAX和DOM。确认XML的合法性的规范是DTD (最初的机制，作为XML规范被定义)和不同的概要标准提案（一种更新的机制，它使用XML语法来做描述合法性准则的工作）。&nbsp; <BR></P>
<P>XML和相关规格：消化字母汤（“字母汤”alphabet soup是一种罐头汤，里面是切成ABC字母图案的细面条，让小朋友边喝汤边把里面的字母排成单词来玩）</P>
<P>现在，你已经对XML有了一个基本的理解，需要对与XML相关的缩写单词代表的意义有一个更深的认识。有很多的基于XML的工作要做，所以你需要学习很多的知识。</P>
<P>目前访问XML文档的API分成两种访问方式：顺序的和任意的访问，分别是：SAX和DOM。确认XML的合法性的规范是DTD (最初的机制，作为XML规范被定义)和不同的概要标准（Schema Standards）提案（一种更新的机制，它使用XML语法来做描述合法性准则的工作）。</P>
<P>其它的一些未来的标准已经接近完成，包括XSL标准（一种建立XML文档转换-例如XML文档转换到HTML或其他的XML文档， 和指示文档如何提交的机制）。XSL标准的转换部分（XSLT (+XPATH)）已经完成并且包含在本指南中。其他的接近完成的成果是XML连接语言（XML Link Language）规范（XML Linking），这个规范可以在XML文档之间做连接。</P>
<P>以上这些就是你将要了解的初步内容。这部分也纵览了一些其它的感兴趣的提案，包括HTML类似标准（HTML-lookalike standard）-- XHTML 和描述XML包含的信息的元标准-- RDF。有些标准成果扩展了XML的能力，比如Xlink和Xpointer。</P>
<P>最后，有很多的感兴趣的标准和标准提案建立在XML之上，包含同步的多媒体综合语言（Synchronized Multimedia Integration Language）-- (SMIL)，数学标记语言（Mathematical Markup Language）-- MathML ，可伸缩的向量图形（Scalable Vector Graphics）-- SVG，DrawML和许多的电子商务标准。</P>
<P>余下的部分使你了解这些初步的更详细的细节。为了保持连贯性，这部分被分为如下方面：<BR>1. 基本标准<BR>2. 概要标准<BR>3. 连接和显示标准<BR>4. 知识标准<BR>5. 建立在XML上的标准<BR>基本标准<BR>一些你需要熟悉的基本标准。<BR>SAX<BR>针对XML的简单API(Simple API for XML)</P>
<P>这个API实际上一个协作产品，它不是W3C的产品。它被包含在里面是因为它拥有W3C建议的特征。</P>
<P>你也可以把这个标准想象为XML的连续访问协议。它拥有在服务器中读写XML数据的最快的执行机制。也可以称它为事件驱动协议，因为这个技术将在SAX parser中注册你的操作，注册完后，当parser看到一个新的XML标记时（或者遇到一个错误，或者parser想告诉你其他的事情），parser就会调用你的回调方法。</P>
<P>DOM</P>
<P>文档对象模型（Document Object Model）<BR>文档对象模型协议将一个XML文档转换成一个对象集。然后你可以操作随意的操作这个对象模型。这个机制被称为“随意访问”协议，因为你能够在任何时候访问数据的任意部分。然后，你可以修改、删除和插入数据。</P>
<P>JDOM and dom4j<BR>当文档对象模型（DOM）提供非常强大的面向文档的处理的时候，它却不能提供更多的面向对象的简化。Java开发者会做更多的面向数据处理，而不是面向书籍、文章和其他的完全的文档，他们发现面向对象的API（如:JDOM和dom4j）更容易使用和符合他们的需要。</P>
<P>下面是JDOM和dom4j的不同：<BR>1. JDOM是更整齐、更小巧的API。在代码风格（coding style）重要的时候，JDOM是一个不错的选择。<BR>2. JDOM是一个Java团体处理（Java Community Process--JCP）初步。当完成时，它将是一个认可的标准。<BR>3. dom4j是一个更小巧、更快的实现，它使用的范围很广。<BR>4. dom4j是一个基于制造商（factory-based）的实现。它更容易修改复杂的、特殊目的的应用程序。在dom4j编写的时候，JDOM还没有使用利用工厂（factory）来事例化分析器对象。所以，使用JDOM，你总是得到原始的分析器。</P>
<P>关于JDOM的更多的信息：<A href="http://www.jdom.org/">http://www.jdom.org/</A>.<BR>关于dom4j的更多的信息：<A href="http://dom4j.org/">http://dom4j.org/</A>.</P>
<P>DTD<BR>文档类型定义（Document Type Definition）</P>
<P>DTD规范是XML规范的一部分，而不是一个单独的实体。另一方面，它是可选的?你可以一个没有DTD的XML文档。有很多的概要（Schema）标准提案来提供更灵活的规范。所以，你完全可以将它看成是单独的部分。</P>
<P>一个DTD规范了不同种类的XML标识和合法的标识的布局。你能够使用DTD来确定你不能产生一个非法的XML结构。你也能够使用它来确定你正在读取得XML结构是合法的。</P>
<P>不幸的是，为一个复杂的文档指定一个DTD是非常的困难的。所以构建一个DTD在某种意义上讲是一门艺术。DTD可以出现在文档的前端，作为序的一部分。它也可以作为单独的一个实体，或者在文档的序和外部的实体中同时存在。</P>
<P>命名空间<BR>命名空间标准允许你用两个或更多的XML标记集合编写XML文档。例如你产生一个基于XML的列表，这个列表使用的标记是另一个厂商提供的。在整个XML文档中“price”数据可能会很多，然而，你只是想显示针对一个结构的“price”数据。命名空间规范定义了一个限定名称的机制，这样可以避免混淆。这样我们写的程序就可以正确的处理文档中的信息。</P>
<P>最新的命名空间信息：<A href="http://www.w3.org/TR/REC-xml-names">http://www.w3.org/TR/REC-xml-names</A>.</P>
<P>XSL<BR>扩展样式表语言（Extensible Stylesheet Language）</P>
<P>XML标准指定怎样识别数据，而不是怎样显示数据。从另一方面来说，HTML是告诉数据怎样显示而不是怎样识别数据。XSL标准有两部分，XSLT和XSL-FO。XSL-FO为你提供页面上的多区域定义并将他们连接到一起。当一个文本流被导向这个集合，它首先流向第一个区域，在第一个区域被充满后会导向第二个区域。像这样的对象被使用在新闻组、一览表和杂志出版中。</P>
<P>最新的XSL标准：<A href="http://www.w3.org/TR/WD-xsl">http://www.w3.org/TR/WD-xsl</A></P>
<P>XSLT(+XPATH)<BR>针对转换的扩展样式表语言（Extensible Stylesheet Language for Transformations）</P>
<P>XSLT的转换标准本质上来讲是一个转换机制，它让你指定将一个XML标记转换成什么并用来显示。例如，转换成HTML标识。不同的XSL格式能够以不同的方式显示同样的数据。（XPATH标准是一个寻址机制，当你构造转换指令示会用到它。）</P>
<P>概要标准<BR>一个DTD使验证相关的简单XML文档是否合法成为可能，但是做起来很难。</P>
<P>一个DTD不能限制元素中的内容，不能指定复杂的关系。例如，指定一个&lt;book&gt;中的&lt;heading&gt;必须包含有&lt;title&gt;和&lt;author&gt;，而一个&lt;chapter&gt;中的&lt;heading&gt;紧紧需要一个&lt;title&gt;。在DTD中，你只能一次的指定&lt;heading&gt;的结构。DTD不是上下文敏感的，因为DTD规范不是层次的。</P>
<P>例如，一个邮件地址包含几个“解析的字符数据（PCDATA）”元素，DTD可能会像下面这样：<BR>&lt;!ELEMENT mailAddress (name, address, zipcode)&gt;<BR>&lt;!ELEMENT name (#PCDATA)&gt;<BR>&lt;!ELEMENT address (#PCDATA)&gt;<BR>&lt;!ELEMENT zipcode (#PCDATA)&gt;<BR>像你看到的那样，这个规范是线性的。所以，在DTD中，如果你需要另一个“name”元素，那么你需要为它定义一个不同的识别符。你不能简单的称它为“name”，因为它会和&lt;mailAddress&gt;元素中使用的“name”冲突。</P>
<P>另一个问题是DTD的无层次性使得理解其中的注解很困难。一个在最上面的注解看起来更像是针对全文结构的注解，但也可能它只是对第一条的注解。最后，DTD不允许你形式上的指定合法域准则，例如对邮政编码域中的5位数限制。</P>
<P>最后，一个DTD使用的语法不同于XML，所以它不能被一个标准的XML分析器解析。这就意味着你不能将DTD读进DOM，和编辑、修改它。</P>
<P>为了修补这些缺点，很多的提案已经被提交。像层次性的“概要--schema”，它指定了合法性准则。后面介绍了一些主要的提案。</P>
<P>XML概要（Schema）<BR>一个大而复杂的标准，它拥有两部分。一部分指定结构联系（这是最大最复杂的部分）。另一部分指定XML元素中的内容的合法性机制，可以通过为每个元素指定一个数据类型。一个好消息是，XML概要可以指定任何设想的关联。坏消息是他很难实现也很难学习。</P>
<P>XML概要的更多的信息，可以查看W3C specs XML Schema(Structures) and XML Schema(Datatypes).<BR><A href="http://www.w3c.org/XML/Schema">http://www.w3c.org/XML/Schema</A>.</P>
<P>RELAX NG<BR>XML的规则语言描述（下一代）--（Regular Language description for XML（Next Generation））</P>
<P>RELAX NG是一个新兴标准，它要比XML结构概要简单。再不远的将来它也可能成为一个ISO标准。</P>
<P>RELAX NG使用规则表达式来表达结构关系上的限制，它使用XML概要数据类型机制来表达内容上的限制。这个标准也使用XML语法，它包含一个DTD到RELAX的转换器。（说它是“next generation”是因为它是结和TREX 的RELAX概要机制的新版本）。</P>
<P>关于RELAX NG的更多的信息：<A href="http://www.oasis-open.org/committees/relax-ng/">http://www.oasis-open.org/committees/relax-ng/</A></P>
<P>TREX<BR>针对XML的树型规则表达式（Tree Regular Expressions for XML）</P>
<P>它是一个通过为XML文档中的结构和内容描述一个模版来表达合法性准则的手段。现在作为RELAX　NG的一部分。</P>
<P>SOX<BR>面向对象的概要（Schema for Object-oriented XML）</P>
<P>SOX是一个概要提案，它包括扩展数据类型、命名空间和嵌入的文档。</P>
<P>SOX更多的信息：<A href="http://www.ascc.net/xml/resource/schematron/schematron.html">http://www.ascc.net/xml/resource/schematron/schematron.html</A>.</P>
<P>连接和表现标准<BR>可以证明，HTML中的文档之间的连接和简单的文档格式是最大的优点。下面的标准定位于在XML中保留HTML的好处并增加额外的功能。</P>
<P>XML Linking<BR>这些规范提供一个不同的连接能力，但对XML的使用上形成了一次很大的冲击。</P>
<P>XLink<BR>Xlink协议是一个在XML文档间处理连接的规范。这个规范允许一些恰当的改进连接，包含双向连接、连接到多文档、展开连接、内部指向连接等。</P>
<P>XML 基础<BR>这个标准定义一个XML文档属性，这个属性定义一个“基础”地址，“基础”地址在查找一个关联的地址时被使用。（例如，一个简单的文件名可以在一个基础地址目录中被找到）</P>
<P>XPointer<BR>一般情况下，Xlink规范是将使用它的ID的文档或文档片断作为目标的。XPointer规范定义的机制是针对“在一个XML文档内部结构的寻址”的。XPointer规范不需要文档的作者为一个文档片断定一个ID。</P>
<P>XML连接的更多的信息：<A href="http://www.w3.org/XML/Linking">http://www.w3.org/XML/Linking</A>. <BR>XHTML<BR>XHTML规范是XML文档的一个用途，只不过XML文档更像HTML文档。既然XML文档可以包含任意的标识，那么为什么不能定义一个很像HTML标识的XML标识集呢？这就是XHTML的想法。这个规范的结果是一个文档既能在浏览器中显示，又能作为XML文档被处理。尽管它并不是纯正的XML，但是它比HTML更容易被处理。</P>
<P>例如，在一个格式良好的XML文档中，每一个标识必须有一个结束标识。所以在XHTML中，你可能看到&lt;p&gt;..&lt;/p&gt;，或&lt;p/&gt;。但你绝对看不到单独的&lt;p&gt;。</P>
<P>XHTML规范是一个HTML4.0到XML的再形成。最新的信息在：<A href="http://www.w3.org/TR/xhtml1">http://www.w3.org/TR/xhtml1</A>.</P>
<P>知识标准<BR>RDF<BR>资源描述框架（Resource Description Framework）</P>
<P>RDF是一个定义元数据的标准，元数据是描述一个特殊的数据条目是什么的信息并指定怎么样来使用这个数据条目。例如，联合XHTML规范或HTML页面来使用，RDF被用来描述页面的内容。例如，如果你的浏览器存储你的ID信息像FIRSTNAME、LASTNAME和EMAIL，一个RDF描述传输传输个需要NAME和EMAILADDRESS的应用程序相应的信息。想象一下，到那一天，你可能不需要在每个网站里输入你的名字和邮件地址。</P>
<P>关于RDF更多的信息：<A href="http://www.w3.org/TR/REC-rdf-syntax">http://www.w3.org/TR/REC-rdf-syntax</A></P>
<P>RDF概要<BR>RDF概要允许相容性原则和附加信息的规范。</P>
<P>更多的RDF概要信息：<A href="http://www.w3.org/TR/rdf-schema">http://www.w3.org/TR/rdf-schema</A>.</P>
<P>XTM<BR>XML主题映射（XML Topic Maps）</P>
<P>在许多方面，主体映射标准是一个比RDF更简单的、更容易使用得知识表示，它更应该值得被关注。迄今为止，RDF是一个W3C标准，但是，主题映射可能只能作为一种开发者选择的标准。</P>
<P>XML主题映射更多的信息：<A href="http://www.topicmaps.org/xtm/index.html">http://www.topicmaps.org/xtm/index.html</A>.</P>
<P>建立在XML上的标准<BR>下面的标准和提案都是建立在XML之上的。因为XML基本上是一个定义语言的工具，所以这些标准使用它定义特殊目的标准语言。</P>
<P>扩展文档标准（Extended Document Standards）<BR>这些标准定义了使用XML产生极端复杂的文档的机制。</P>
<P>SMIL<BR>同步的多媒体综合语言（Synchronized Multimedia Integration Language）<BR>SMIL是一个W3C建议的标准，它涵盖了音频、视频和动画。</P>
<P>更多关于SMIL的信息：<A href="http://www.w3.org/TR/REC-smil">http://www.w3.org/TR/REC-smil</A>.</P>
<P>MathML<BR>数学标记语言（Mathematical Markup Language）</P>
<P>MathML是一个W3C建议的标准，它处理数学公式的显示。</P>
<P>更多关于MathML的信息：<A href="http://www.w3.org/TR/REC-MathML">http://www.w3.org/TR/REC-MathML</A>.</P>
<P>SVG<BR>可伸缩的向量图像（Scalable Vector Graphics）</P>
<P>SVG是一个W3C工作草案，它覆盖了向量图像的显示。</P>
<P>更多关于SVG的信息：<A href="http://www.w3.org/TR/WD-SVG">http://www.w3.org/TR/WD-SVG</A>.</P>
<P>DrawML<BR>绘制元语言（Drawing Meta Language）</P>
<P>DrawML是一个W3C的附注，它涵盖了技术图表的2D图像。</P>
<P>更多的关于DrawML的信息：<A href="http://www.w3.org/TR/NOTE-drawml">http://www.w3.org/TR/NOTE-drawml</A>.</P>
<P>电子商务标准（eCommerce Standards）</P>
<P>这些标准定位于在B2B和B2C的范围里使用XML。</P>
<P>ICE<BR>信息和内容交换（Information and Content Exchange）</P>
<P>ICE是一个被内容经营者使用的协议。它的焦点在“在传统出版和B2B联系上自动化内容转换和重用”</P>
<P>更多关于ICE的信息：<A href="http://www.w3.org/TR/NOTE-ice">http://www.w3.org/TR/NOTE-ice</A>.</P>
<P>ebXML<BR>基于XML的电子交易（Electronic Business with XML）</P>
<P>这个标准定位在使用XML建立模块化的电子交易框架。它是UN/CEFACT的一个起步产品。</P>
<P>更多关于ebXML的信息：<A href="http://www.ebxml.org/">http://www.ebxml.org/</A>.</P>
<P>cxml<BR>商业XML（Commerce XML）</P>
<P>cxml是一个RosettaNet（<A href="http://www.rosettanet.org">www.rosettanet.org</A>）标准，它的目的是为了不同的买主建立一个在线的名单。</P>
<P>更多关于cxml的信息：<A href="http://www.cxml.org/">http://www.cxml.org/</A></P>
<P>CBL<BR>通用交易库（Common Business Library）</P>
<P>CBL是一个元素和属性定义的库。</P>
<P>更多关于CBL的信息：<A href="http://www.commerce.net/projects/currentprojects/eco/wg/eCo_Framework_Specifications.html">http://www.commerce.net/projects/currentprojects/eco/wg/eCo_Framework_Specifications.html</A>.<BR>UBL<BR>统一商业语言（Universal Business Language）</P>
<P>一个OASIS初步定位在编译一个XML交易文档的标准库。</P>
<P>更多关于UBL的信息：<A href="http://www.oasis-open.org/committees/ubl">http://www.oasis-open.org/committees/ubl</A>.</P>
<P>总结<BR>XML是一个正在被广泛的应用的标准，它正在被用在不同的领域。<BR></P><img src ="http://www.blogjava.net/Victor/aggbug/27524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2006-01-11 10:07 <a href="http://www.blogjava.net/Victor/articles/27524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>翻译了篇AJAX的文章Usint AJAX With JAVA Technology(转)</title><link>http://www.blogjava.net/Victor/articles/26055.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 30 Dec 2005 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/26055.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/26055.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/26055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/26055.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/26055.html</trackback:ping><description><![CDATA[<P>闲着没事，在SUN.COM上看到一篇AJAX的文章，感觉不错，所以试着译成了中文。许多地方自己也把握不也作者的意思，加上个人英文水平不咋地，可能许多不正确的地方，希望大家指正。<BR>样例安装的一段没有翻译，感觉没有必要～～（例子中没有附JSTL包，要自己到APACHE下载安装。）</P>
<P>原文地址：<A href="http://java.sun.com/developer/EJTechTips/2005/tt1122.html#1">http://java.sun.com/developer/EJTechTips/2005/tt1122.html#1</A></P>
<P>by Greg Murray<BR>作者：格雷格－默里<BR>AJAX stands for Asynchronous JavaScript and XML. In essence, AJAX is an efficient way for a web application to handle user interactions with a web page -- a way that reduces the need to do a page refresh or full page reload for every user interaction. This enables rich behavior (similar to that of a desktop application or plugin-based web application) using a browser. AJAX interactions are handled asynchronously in the background. As this happens, a user can continue working with the page. AJAX Interactions are initiated by the JavaScript in the web page. When the AJAX interaction is complete, JavaScript updates the HTML source of the page. The changes are made immediately without requiring a page refresh. AJAX interactions can be used to do things such as validate form entries (while the user is entering them) using server-side logic, retrieve detailed data from the server, dynamically update data on a page, and submit partial forms from the page.<BR>AJAX代表着异步的JAVASCRIPT和XML。本质上，AJAX是一种在WEB应用中通过网页与用户交互的有效方式，它减少了用户交互中刷新页面或整页重载的频率。这样使用浏览器可以进行更丰富的动作（与桌面应用或基于PLUGIN的WEB应用相似）。AJAX交互在后台异步执行，此时用户可以继续在这个页面上工作。AJAX是由网页中的JAVASCRIPT驱动的。当AJAX交互完成后，JS更新网页HTML代码。这个动作在不需要刷新页面的情况下迅速执行。AJAX可以用于许多方面，例如在用户输入的同时用服务器端规则验证其输入，获得服务器端详细数据并主动更新页面数据，或者提交页面表单的部分数据。<BR>What is particularly attractive about this is that AJAX applications do not require a separate plug-in, and are platform and browser-neutral. That said, AJAX is not supported as well in older browsers. Care needs to be taken in writing client-side script that accounts for the differences between browsers. You might consider using a JavaScript library that abstracts the browser differences and in some cases support older browsers using alternative interaction techniques. For more details, see the AJAX FAQ for the Java Developer <BR>是什么让AJAX技术具体如此魅力呢？AJAX应用不需用单独的插件，并且是平台和浏览器中立的。也就是说，AJAX并不被旧的浏览器良好支持。但是不必太过担心，你可以撰写跨不同浏览器的脚本。你应该考虑使用跨不同浏览器的JS库并且在一些情况下有选择性的使用交互技术以支持旧的浏览器。了解更多，请查看JAVA开发者的AJAX&nbsp; FAQ。<A href="http://weblogs.java.net/blog/gmurray71/">http://weblogs.java.net/blog/gmurray71/</A></P>
<P>So Where Does Java Technology Fit In?<BR>那么JAVA技术适合在哪儿使用呢？<BR>Java technology and AJAX work well together. Java technology provides the server-side processing for AJAX interactions. It can provide this through servlets, JavaServer Pages (JSP) technology, JavaServer Faces (JSF) technology, and web services. The programming model for handling AJAX requests uses the same APIs that you would use for conventional web applications. JSF technology can be used to create reusable components that generate the client-side JavaScript and corresponding server-side AJAX processing code. Let's look at an example that uses AJAX and servlets. <BR>JAVA技术与AJAX搭配使用非常不错。JAVA技术为AJAX交互提供服务器端处理，通过servlets、JavaServer Pages (JSP)、JavaServer Faces (JSF) 及web服务。AJAX请求的编程模式使用与常规WEB应用相同的API。JSF技术可以用来创建可重用的组件，这些组件生成客户端JS并与服务器端AJAX处理代码通信。下面让我们来看一个使用AJAX和SERVLETS的例子。<BR>Autocomplete Example<BR>实例：自动完成<BR>Imagine a web page in which a user can search for information about an employee. The page includes a field where the user can enter the name of the employee. In this example the entry field has an autocomplete feature. In other words, the user can type in part of the employee name, and the web application attempts to complete the name by listing all employees whose first or last name begins with the characters entered. The autocomplete feature saves the user from having to remember the complete name of the employee or from looking for the name on another page. <BR>假设用户在一个网页中搜索雇员信息。这个页面包含一个输入域，用户在其中输入雇员的名字。在这个例子中输入域具有自动完成的功能。换句话说，用户输入雇员名的一部分，WEB应用通过列出所有姓名以输入字母开头的雇员来帮助完成输入。自动完成功能让用户可以无需记住员工的全名或者从另一个页面查找名字。（此句偶之前百思不得其解，后来经永华指点，大约是这样的：你可以有三种方式查询员工信息，1.记住员工的名字、2.从别的地方，比如其它页面找到后复制粘贴过来，3.使用自动完成功能，很显然，第三种是最省事的，优于其它方式，所以说3 SAVE FROM 1/2.）</P>
<P><BR>Implementing autocomplete in a search field is something that can be performed using AJAX. To do it, you need to provide code on the client and on the server.<BR>可以使用AJAX实现搜索输入域的自动完成。要实现这个，需要撰写相应的客户端和服务器端代码。</P>
<P>On the Client<BR>客户端<BR>First, the user specifies the URL of a page that is loaded by the browser. For this example let's assume the page is an HTML page that is generated by a JSF component, servlet, or JSP page. The page contains a form text field that has an attribute onkeyup with the name of a JavaScript function doCompletion(). This function is called each time a key is pressed in the form text field.<BR>首先，用户打开一个网页。假设这个页面是一个由JSF组件、SERVLET或JSP产生的HTML页面。页面中包含一个表单文本域，它有一个ONKEYUP属性，其值为一个JS函数doCompletion()。这个每当文本域有输入改变，这个函数就会被调用。<BR>&nbsp;&nbsp;&nbsp; &lt;input type="text"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size="20"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; autocomplete="off"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="complete-field"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name="id"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onkeyup="doCompletion();"&gt;</P>
<P>Let's assume that a user types in an "M" character in the form text field. In response, the doCompletion() function is called which, in turn, initializes an XMLHttpRequest object:<BR>假设用户在表单文本域输入字母M，doCompletion()将被调用，初始化一个XMLHttpRequest对象。<BR>&nbsp;&nbsp; function initRequest(url) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (window.XMLHttpRequest) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new XMLHttpRequest();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (window.ActiveXObject) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isIE = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ActiveXObject("Microsoft.XMLHTTP");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp; function doCompletion() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (completeField.value == "") {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearTable();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var url = "autocomplete?action=complete&amp;id=" + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; escape(completeField.value);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var req = initRequest(url);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.onreadystatechange = function() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (req.readyState == 4) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (req.status == 200) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parseMessages(req.responseXML);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (req.status == 204){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearTable();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.open("GET", url, true);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.send(null);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp; }</P>
<P>The XMLHttpRequest object is not currently part of standard JavaScript (efforts are underway to standardize it), but is a de facto standard and is the heart of AJAX. This object is responsible for interacting over HTTP with a server-side component (in this case, a servlet).<BR>XMLHttpRequest对象现在还不是JS标准中的一部分（正在努力将其标准化），但它却是事实上的标准并且是AJAX的核心。这个对象对于在HTTP协议上与服务器端组件（此例中是一个SERVLET）交互很可靠.</P>
<P>Three parameters are specified when you create an XMLHttpRequest object: a URL, the HTTP method (GET or POST), and whether or not the interaction is asynchronous. In the XMLHttpRequest example, the parameters are:<BR>当创建XMLHttpRequest对象时需指定三个参数：一个URL,HTTP方式（GET或POST）, 交互是否异步。在这个XMLHttpRequest 例子中，这些参数如下：<BR>&nbsp;&nbsp;&nbsp; * The URL autocomplete, and the text from the complete-field (an M character):<BR>*自动完成的URL和从输入域键入的文字：<BR>&nbsp;&nbsp;&nbsp;&nbsp; var url = "autocomplete?action=complete&amp;id=" + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; escape(completeField.value);</P>
<P>&nbsp;&nbsp;&nbsp; * GET, signifying the HTTP interactions uses the GET method, and true, signifying that the interaction is asynchronous:<BR>*GET，表示HTTP交互使用GET方法；TRUE，表示交互是异步的：<BR>&nbsp;&nbsp;&nbsp;&nbsp; req.open("GET", url, true);</P>
<P>A callback function needs to be set when you use asynchronous calls. This callback function is called asynchronously at specific points during HTTP interaction when the readyState property on the XMLHttpRequest changes. In the example the callback function is processRequest(). It's set as the XMLHttpRequest.onreadystatechange property to a function. Notice the call to the parseMessages function when the readState is "4". The XMLHttpRequest.readyState of "4" signifies the successful completion of the HTTP interaction.<BR>当使用异步调用时，需要建立一个回调函数。HTTP交互中当XMLHttpRequest中的readyState属性改变时，回调函数被异步调用。此例中回调函数是processRequest()。它为一个函数建立了一个XMLHttpRequest.onreadystatechange属性。（前两句还不太明白什么意思——译者注）。注意当readyState为4时对parseMessages函数的调用。XMLHttpRequest.readyState为4意味着HTTP交互的成功完成。<BR>The HTTP interaction begins when XMLHttpRequest.send() is called. If the interaction is asynchronous, the browser continues to process events in the page. <BR>HTTP交互以调用XMLHttpRequest.send()开始。如果交互是异步的，浏览器前继续执行页面事件（而不是中断用户当前动作去执行交互动作——译者注）。<BR>On the Server<BR>服务器端<BR>The XMLHttpRequest makes an HTTP GET request to the URL autocomplete, which is mapped to a servlet called AutoComplete. The doGet() method of the AutoComplete servlet is called. Here is what the doGet() method looks like: <BR>XMLHttpRequest为自动完成的URL产生了一个HTTP GET请求，这个URL被映射到一个名为AutoComplete的Servlet上。AutoComplete servlet的doGet() 方法被调用。这里的doGet()方法如下：<BR>&nbsp;&nbsp; public void doGet(HttpServletRequest request, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletResponse response) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException, ServletException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String targetId = request.getParameter("id"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator it = employees.keySet().iterator(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (it.hasNext()) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EmployeeBean e = (EmployeeBean)employees.get(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (String)it.next()); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((e.getFirstName().toLowerCase().startsWith(targetId) || <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.getLastName().toLowerCase().startsWith(targetId)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !targetId.equals("")) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append("&lt;employee&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append("&lt;id&gt;" + e.getId() + "&lt;/id&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append("&lt;firstName&gt;" + e.getFirstName() + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;/firstName&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append("&lt;lastName&gt;" + e.getLastName() + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;/lastName&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.append("&lt;/employee&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; namesAdded = true; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (namesAdded) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setContentType("text/xml"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setHeader("Cache-Control", "no-cache"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.getWriter().write("&lt;employees&gt;" + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sb.toString() + "&lt;/employees&gt;"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setStatus(HttpServletResponse.SC_NO_CONTENT); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp; }</P>
<P>As you can see in this servlet, there is nothing really new you need to learn to write server-side code for AJAX processing. The response content type needs to be set to text/xml for cases where you want to exchange XML documents. With AJAX, you can also exchange plain text or even snippets of JavaScript which may be evaluated or executed by the callback function on the client. Note too that some browsers might cache the results, and so it might be necessary to set the Cache-Control HTTP header to no-cache. In this example, the servlet generates an XML document that contains all employees with a first or last name beginning with the character M. Here is an example of an XML document that is returned to the XMLHttpRequest object that made the call: <BR>如你在SERVLET中所看到，不需要为写服务器端AJAX处理代码而学习新知识。当你想要发送XML文档，只需将响应内容类型设置为text/xml。使用AJAX，你甚至可以发送普通文本或者小段JS代码，这些代码可能在客户端被回调函数计算或执行。注意：一些浏览器可能将结果缓存，所以有必要设置Cache-Control HTTP header为no-cache。此例中，SERVLET产生一个XML文档，它包含所有姓或名以M开头的雇员的姓名。下面是一个返回给XMLHttpRequest对象的XML文档例子：<BR>&nbsp;&nbsp; &lt;employees&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;3&lt;/id&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;firstName&gt;George&lt;/firstName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lastName&gt;Murphy&lt;/lastName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;2&lt;/id&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;firstName&gt;Greg&lt;/firstName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lastName&gt;Murphy&lt;/lastName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;11&lt;/id&gt;&lt;firstName&gt;Cindy&lt;/firstName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lastName&gt;Murphy&lt;/lastName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;4&lt;/id&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;firstName&gt;George&lt;/firstName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lastName&gt;Murray&lt;/lastName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;employee&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;1&lt;/id&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;firstName&gt;Greg&lt;/firstName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lastName&gt;Murray&lt;/lastName&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/employee&gt;<BR>&nbsp;&nbsp; &lt;/employees&gt;<BR>Returning to the Client<BR>再来看客户端<BR>When the XMLHttpRequest object that made the initial call receives the response, it calls the parseMessages() function (see the initialization of the XMLHttpRequest earlier in this example for more details). Here is what the parseMessages() function looks like:<BR>当最初发送请求的XMLHttpRequest对象收到回应，它调用parseMessages() 函数（参见此例前面XMLHttpRequest的初始化）。这个函数如下：<BR>&nbsp;&nbsp; function parseMessages(responseXML) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearTable();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var employees = responseXML.getElementsByTagName(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "employees")[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (employees.childNodes.length &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; completeTable.setAttribute("bordercolor", "black");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; completeTable.setAttribute("border", "1");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearTable();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (loop = 0; loop &lt; employees.childNodes.length; loop++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var employee = employees.childNodes[loop];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var firstName = employee.getElementsByTagName(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "firstName")[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var lastName = employee.getElementsByTagName(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "lastName")[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var employeeId = employee.getElementsByTagName(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "id")[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; appendEmployee(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; firstName.childNodes[0].nodeValue,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lastName.childNodes[0].nodeValue, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; employeeId.childNodes[0].nodeValue);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp; }</P>
<P>The parseMessages() function receives as a parameter an object representation of the XML document returned by the AutoComplete servlet. The function programmatically traverses the XML document, and then uses the results to update the contents of the HTML page. This is done by injecting into a &lt;div&gt; element whose id is "menu-popup" the HTML source for the names in the XML document:<BR>parseMessages() 函数接收AutoComplete servlet返回的XML文档对象做为参数。该函数自动转化XML文档并更新网页内容，通过在HTML源文件中为XML文档中的姓名插入一个 id 为 "menu-popup"的DIV元素：<BR>&nbsp;&nbsp; &lt;div style="position: absolute; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; top:170px;left:140px" id="menu-popup"&gt;</P>
<P>As the user enters more characters, the list shortens. The user can then click on one of the names.<BR>当用户输入更多字符，姓名列表变短。用户可以点击要查找的姓名。<BR>Hopefully by now you realize that AJAX is simply exchanging information over HTTP in the background of a page, and updating that page dynamically based on the results. For more information about AJAX and Java technology, see the technical article Asynchronous JavaScript Technology and XML (AJAX) With Java 2 Platform, Enterprise Edition. （<A href="http://java.sun.com/developer/technicalArticles/J2EE/AJAX/index.html">http://java.sun.com/developer/technicalArticles/J2EE/AJAX/index.html</A>）Also see the AJAX BluePrints page, （<A href="http://java.sun.com/blueprints/ajax.html">http://java.sun.com/blueprints/ajax.html</A>）and the AJAX FAQ for the Java Developer in Greg Murray's blog. （<A href="http://weblogs.java.net/blog/gmurray71/">http://weblogs.java.net/blog/gmurray71/</A>）<BR>希望现在你已经了解到AJAX如何在后台简单地交换信息以及在此基础上动态地更新页面。欲了解更多关于AJAX和JAVA技术，可以看这篇技术文章《Asynchronous JavaScript Technology and XML (AJAX) With Java 2 Platform, Enterprise Edition》（<A href="http://java.sun.com/developer/technicalArticles/J2EE/AJAX/index.html">http://java.sun.com/developer/technicalArticles/J2EE/AJAX/index.html</A>）。或者看AJAX蓝图<A href="http://java.sun.com/blueprints/ajax.html">http://java.sun.com/blueprints/ajax.html</A>，或Greg Murray的博客中为JAVA开发者写的AJAX FAQ。<A href="http://weblogs.java.net/blog/gmurray71/">http://weblogs.java.net/blog/gmurray71/</A><BR>About the Author<BR>关于作者<BR>Greg Murray is the servlet specification lead. Greg is a former member of the Java BluePrints team, and was responsible for the web tier recommendations. He is leading the AJAX effort at Sun with the help of the BluePrints team. Greg has experience with internationalization, web services, J2SE standalone clients, and AJAX-based web clients. <BR>格雷格－默里是SERVLET规范领导。他曾是JAVA蓝图工作组会员，非常负责地对WEB TIER提出许多建议(或者是对XX负责)。在蓝图工作组的协助下他在SUN领导AJAX工作。格雷格在国际化、WEB服务、J2SE独立客户端和基于AJAX的WEB客户端方面有着丰富的经验。</P>
<P>About the Translater<BR>关于译者<BR>Ahdong is wide-interested boy in China.Ahdong is a member of Network Creative Lab of Huaqiao University,and is&nbsp; responsible for the newbees' recommendations about learning.He is leading the software department at the lab with Younghua.Ahdong is interested in and has some experience with Linux,web development with JAVA or PHP,web standard,and AJAX-based web clients.<BR>阿冬是一个兴趣广泛的中国男孩。他是华侨大学网络创新实验室的一员，负责任地对许多初学者提了许多中肯的建议。他和永华一起领导实验室软件部。阿冬在LINUX、PHP或JAVA WEB开发、WEB标准、基于AJAX的WEB客户端方面有着深厚的兴趣并多少有一点儿经验。<BR></P><img src ="http://www.blogjava.net/Victor/aggbug/26055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-30 14:43 <a href="http://www.blogjava.net/Victor/articles/26055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java更新XML的四种常用方法简介</title><link>http://www.blogjava.net/Victor/articles/23756.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 14 Dec 2005 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23756.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23756.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23756.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23756.html</trackback:ping><description><![CDATA[<DIV class=comText><FONT size=3>本文简要的讨论了Java语言编程中更新XML文档的四种常用方法,并且分析这四种方法的优劣。其次,本文还对如何控制Java程序输出的XML文档的格式做了展开论述。<BR><BR>　　JAXP是Java API for XML Processing的英文字头缩写,中文含义是:用于XML文档处理的使用Java语言编写的编程接口。JAXP支持DOM、SAX、XSLT等标准。为了增强JAXP使用上的灵活性,开发者特别为JAXP设计了一个Pluggability Layer,在Pluggability Layer的支持之下,JAXP既可以和具体实现DOM API、SAX API 的各种XML解析器(XML Parser,例如Apache Xerces)联合工作,又可以和具体执行XSLT标准的XSLT处理器(XSLT Processor,例如Apache Xalan)联合工作。应用Pluggability Layer的好处在于:我们只需要熟悉JAXP各个编程接口的定义即可,而不需要对所采用的具体的XML解析器、XSLT处理器有很深入的了解。比如在某个Java程序中,通过JAXP调用XML解析器Apache Crimson对XML文档进行处理,如果我们希望使用别的XML解析器(比如Apache Xerces),以便提高该程序的性能,那么原程序代码可能不需要任何改变,直接就可以使用(你所需要做的事情只是将包含Apache Xerces代码的jar文件加入到环境变量CLASSPATH中,而将包含Apache Crimson代码的jar文件在环境变量CLASSPATH中删除)。 <BR><BR>　　目前JAXP已经应用的十分普遍了,可以说是Java语言中处理XML文档的标准API。有些初学者在学习使用JAXP的过程中,经常会提出这样的问题:我编写的程序对DOM Tree做了更新,但是当程序退出以后,原始的XML文档并没有改变,还是老样子,如何实现对原始XML文档和DOM Tree的同步更新呢?咋一看来,在JAXP中似乎没有提供相应的接口/方法/类,这是很多初学者都感到困惑的问题。本文的主旨就在于解决这个问题,简单的介绍几种常用的同步更新原始XML文档和DOM Tree的方法。为了缩小讨论的范围,本文所涉及的XML解析器仅包括Apache Crimson和Apache Xerces,而XSLT处理器仅仅使用Apache Xalan。 <BR><BR>　　方法一:直接读写XML文档 <BR><BR>　　这也许是最笨最原始的办法了。当程序获取DOM Tree之后,应用DOM模型的Node接口的各个方法对DOM Tree进行更新,下一步应该对原始的XML文档进行更新了。我们可以运用递归的办法或者是应用TreeWalker类,遍历整个DOM Tree,与此同时,将DOM Tree的每一个节点/元素依次写入到预先打开的原始XML文档中,当DOM Tree被遍历完全之后,DOM Tree和原始的XML文档就实现了同步更新。实际中,这个方法极少使用,不过如果你要编程实现自己的XML解析器,这种方法还是有可能用得上的。<BR><BR>方法二:使用XmlDocument类 <BR><BR>　　使用XmlDocument类?JAXP中分明没有这个类呀!是不是作者搞错了?没有错!就是使用XmlDocument类,确切的说,是使用XmlDocument类的write()方法。 <BR><BR>　　在上文已经提到过,JAXP可以和各种各样的XML解析器联合使用,这次我们选用的XML解析器是Apache Crimson。XmlDocument(org.apache.crimson.tree.XmlDocument)是Apache Crimson的一个类,并不包含于标准的JAXP中,难怪在JAXP的文档中找不到XmlDocument类的芳踪呢。现在问题出来了,如何应用XmlDocument类来实现更新XML文档的功能?在XmlDocument类中提供了下面三个write()方法(根据Crimson最新的版本------Apache Crimson 1.1.3): <BR><BR></FONT><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD><FONT size=3>public void write (OutputStream out) throws IOException<BR>public void write (Writer out) throws IOException<BR>public void write (Writer out, String encoding) throws IOException</FONT></TD></TR></TBODY></TABLE><BR>　　上述三个write()方法的主要作用就是输出DOM Tree中的内容到特定的输出介质中,比如文件输出流、应用程序控制台等等。那么又如何使用上述三个write()方法呢?请看下面的Java程序代码片断: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>String name="fancy";<BR>DocumentBuilder parser;<BR>DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();<BR>try <BR>{<BR>parser = factory.newDocumentBuilder();<BR>Document doc = parser.parse("user.xml");<BR>Element newlink=doc.createElement(name);<BR>doc.getDocumentElement().appendChild(newlink);<BR>((XmlDocument)doc).write(new FileOutputStream(new File("xuser1.xml")));<BR>}<BR>catch (Exception e) <BR>{<BR>//to log it <BR>}</TD></TR></TBODY></TABLE><BR>　　在上面的代码中,首先创建了一个Document对象doc,获取完整的DOM Tree,然后应用Node接口的appendChild()方法,在DOM Tree的最后追加了一个新节点(fancy),最后调用XmlDocument类的write(OutputStream out)方法,把DOM Tree中的内容输出到xuser.xml中(其实也可以输出到user.xml,更新原始的XML文档,在这里为了便于做对比,故而输出到xuser.xml文件中)。需要注意的是不能直接对Document对象doc直接调用write()方法,因为JAXP的Document接口并没有定义任何write()方法,所以必须将doc由Document对象强制转换为XmlDocument对象,然后才能调用write()方法,在上面的代码中使用的是write(OutputStream out)方法,这个方法使用缺省的UTF-8编码输出DOM Tree中的内容到特定的输出介质中,如果DOM Tree中包含中文字符,那么输出的结果有可能是乱码,亦即存在所谓的"汉字问题",解决的办法是使用write (Writer out, String encoding)方法,显式指定输出时的编码,例如将第二个参数设为"GB2312",这时即不存在"汉字问题",输出结果能够正常显示中文字符。 <BR><BR>　　完整的例子请参考下列文件: AddRecord.java(见附件)、user.xml(见附件)。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行AddRecord.java这个程序,你需要到网址<A href="http://xml.apache.org/dist/crimson/去下载Apache" target=_blank>http://xml.apache.org/dist/crimson/去下载Apache</A> Crimson,并将所获取的crimson.jar文件加入到环境变量CLASSPATH中。 <BR><BR>　　注意: <BR><BR>　　Apache Crimson的前身是Sun Project X Parser,后来不知何故,由X Parser演变为Apache Crimson,至今Apache Crimson的很多代码都是从X Parser中直接移植过来的。比如上文用到的XmlDocument类,它在X Parser中是com.sun.xml.XmlDocument,到了Apache Crimson中摇身一变,就变成了org.apache.crimson.tree.XmlDocument类,其实它们的绝大部分代码是一样的,可能就package语句和import语句以及文件开头的一段lience有所不同而已。早期的JAXP是和X Parser捆绑在一起的,因此一些老的程序使用了com.sun.xml包,如果你现在重新编译它们,有可能不能通过,肯定就是因为这个原因。后来的JAXP和Apache Crimson捆绑在一起,比如JAXP 1.1,如果你使用JAXP 1.1,那么不需要额外下载Apache Crimson,也能够正常编译运行上面的例子(AddRecord.java)。最新的JAXP 1.2 EA(Early Access)改弦更张,采用性能更好的Apache Xalan和Apache Xerces分别作为XSLT处理器和XML解析器,不能直接支持Apache Crimson了,所以如果你的开发环境采用了JAXP 1.2 EA或者是Java XML Pack(内含JAXP 1.2 EA),那么将无法直接编译运行上面的例子(AddRecord.java),你需要额外下载并安装Apache Crimson。&nbsp;<BR><BR>方法三:使用TransformerFactory和Transformer类 <BR><BR>　　在JAXP中所提供的标准的更新原始XML文档的方法就是调用XSLT引擎,亦即使用TransformerFactory和Transformer类。请看下面的Java代码片断: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>//首先创建一个DOMSource对象,该构造函数的参数可以是一个Document对象<BR>//doc代表更改后的DOM Tree。<BR>DOMSource doms = new DOMSource (doc);<BR><BR>//创建一个File对象,代表DOM Tree所包含的数据的输出介质,这是一个XML文件。<BR>File f = new File ("XMLOutput.xml");<BR><BR>//创建一个StreamResult对象,该构造函数的参数可以取为File对象。<BR>StreamResult sr = new StreamResult (f);<BR><BR>//下面调用JAXP中的XSLT引擎来实现输出DOM Tree中的数据到XML文件中的功能。<BR>//XSLT引擎的输入为DOMSource对象,输出为StreamResut对象。<BR>try <BR>{<BR>//首先创建一个TransformerFactory对象,再由此创建Transformer对象。Transformer<BR>//类相当于一个XSLT引擎。通常我们使用它来处理XSL文件,但是在这里我们使<BR>//用它来输出XML文档。<BR>TransformerFactory tf=TransformerFactory.newInstance(); <BR>Transformer t=tf.newTransformer ();<BR><BR>//关键的一步, 调用Transformer对象 (XSLT引擎)的transform()方法,该方法的第一<BR>//个参数是DOMSource对象,第二个参数是StreamResult对象。<BR>t.transform(doms,sr); <BR>}<BR>catch (TransformerConfigurationException tce)<BR>{ <BR>System.out.println("Transformer Configuration Exception\n-----");<BR>tce.printStackTrace(); <BR>}<BR>catch (TransformerException te) <BR>{ <BR>System.out.println ("Transformer Exception\n---------"); <BR>te.printStackTrace (); <BR>} </TD></TR></TBODY></TABLE><BR>　　在实际的应用中,我们可以应用传统的DOM API从XML文档中获取DOM Tree,然后根据实际的需求对DOM Tree执行各种操作,得到最终的Document对象,接下来可以由此Document对象创建DOMSource对象,剩下的事情就是照搬上面的代码了,程序运行完毕后, XMLOutput.xml就是你所需要的结果(当然了,你可以随意更改StreamResult类构造函数的参数,指定不同的输出介质,而不必是千篇一律的XML文档)。 <BR><BR>　　这个方法最大的好处在于可以随心所欲的控制DOM Tree中的内容输出到输出介质中的格式,但是光靠TransformerFactory类和Transformer类并不能实现这个功能,还需要依赖OutputKeys类的帮助。 完整的例子请参考下列文件: AddRecord2.java(见附件)、user.xml(见附件)。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行AddRecord2.java这个程序,你需要到网址<A href="http://java.sun.com去下载安装jaxp/" target=_blank>http://java.sun.com去下载安装JAXP</A> 1.1或者Java XML Pack(Java XML Pack已经内含JAXP了)。 <BR><BR>　　OutputKeys类 <BR><BR>　　javax.xml.transform.OutputKeys类和java.util.Properties类配合使用,可以控制JAXP的XSLT引擎(Transformer类)输出XML文档的格式。请看下面的代码片断: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>//首先创建一个TransformerFactory对象,再由此创建Transformer对象。<BR>TransformerFactory tf=TransformerFactory.newInstance(); <BR>Transformer t=tf.newTransformer ();<BR><BR>//获取Transformser对象的输出属性,亦即XSLT引擎的缺省输出属性,这是一个<BR>//java.util.Properties对象。<BR>Properties properties = t.getOutputProperties(); <BR><BR>//设置新的输出属性:输出字符编码为GB2312,这样可以支持中文字符,XSLT引擎所输出<BR>//的XML文档如果包含了中文字符,可以正常显示,不会出现所谓的"汉字问题"。<BR>//请留意OutputKeys类的字符串常数OutputKeys.ENCODING。<BR>properties.setProperty(OutputKeys.ENCODING,"GB2312"); <BR><BR>/更新XSLT引擎的输出属性。<BR>t.setOutputProperties(properties); <BR><BR>//调用XSLT引擎,按照输出属性中的设置,输出DOM Tree中的内容到输出介质中。<BR>t.transform(DOMSource_Object,StreamResult_Object); </TD></TR></TBODY></TABLE><BR>　　从上面的程序代码,我们不难看出,通过设置XSLT引擎(Transformer类)的输出属性,可以控制DOM Tree中的内容的输出格式,这对于我们定制输出内容是很有帮助的。那么JAXP的XSLT引擎(Transformer类)有那些输出属性可以设置呢? javax.xml.transform.OutputKeys类定义了很多字符串常数,它们都是可以自由设置的输出属性,常用的输出属性如下所示: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String METHOD </TD></TR></TBODY></TABLE><BR>　　可以设为"xml"、"html"、"text"等值。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String VERSION</TD></TR></TBODY></TABLE><BR>　　所遵循规范的版本号,如果METHOD设为"xml",那么它的值应该设为"1.0",如果METHOD设为"html",那么它的值应该设为"4.0",如果METHOD设为"text",那么这个输出属性会被忽略。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String ENCODING </TD></TR></TBODY></TABLE><BR>　　设置输出时所采用的编码方式,比如"GB2312"、"UTF-8"等等,如果将其设置为"GB2312",可以解决所谓的"汉字问题"。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String OMIT_XML_DECLARATION</TD></TR></TBODY></TABLE><BR>　　设置输出到XML文档中时是否忽略XML声明,亦即类似于: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>＜?xml version="1.0" standalone="yes" encoding="utf-8" ?＞ </TD></TR></TBODY></TABLE><BR>　　这样的代码。它可选的值有"yes"、"no"。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String INDENT</TD></TR></TBODY></TABLE><BR>　　IDENT设定XSLT引擎在输出XML文档时,是否自动添加额外的空格,它可选的值为"yes"、"no"。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>public static final java.lang.String MEDIA_TYPE </TD></TR></TBODY></TABLE><BR>　　MEDIA_TYPE设定输出文档的MIME类型。 <BR><BR>　　如果设定XSLT引擎的输出属性呢?下面我们来总结一下: <BR><BR>　　首先是获取XSLT引擎(Transformer类)的缺省输出属性的集合,这需要使用Transformer类的getOutputProperties()方法,返回值是一个java.util.Properties对象。 <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>Properties properties = transformer.getOutputProperties();</TD></TR></TBODY></TABLE><BR>　　然后是设定新的输出属性,比如: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>properties.setProperty(OutputKeys.ENCODING,"GB2312");<BR>properties.setProperty(OutputKeys.METHOD,"html");<BR>properties.setProperty(OutputKeys.VERSION,"4.0");<BR>……………………………………………………… </TD></TR></TBODY></TABLE><BR>　　最后是更新XSLT引擎(Transformer类)的缺省输出属性的集合,这需要使用Transformer类的setOutputProperties()方法,参数是一个java.util.Properties对象。 <BR><BR>　　我们编写了一个新的程序,其中应用了OutputKeys类,用以控制XSLT引擎的输出属性,该程序的架构和前一个程序(AddRecord3.java)大致相同,不过输出结果略有不同。完整的代码请参考下列文件: AddRecord3.java(见附件)、user.xml(见附件)。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行AddRecord3.java这个程序,你需要到网址<A href="http://java.sun.com去下载安装jaxp/" target=_blank>http://java.sun.com去下载安装JAXP</A> 1.1或者Java XML Pack(Java XML Pack内含JAXP了)。&nbsp;<BR><BR>方法四:使用Xalan XML Serializer <BR><BR>　　方法四其实是方法三的一个变种,它需要Apache Xalan和Apache Xerces的支持才能够运行。例子代码如下所示: <BR><BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1><BR>
<TBODY><BR>
<TR><BR>
<TD>//首先创建一个DOMSource对象,该构造函数的参数可以是一个Document对象<BR>//doc代表更改后的DOM Tree。<BR>DOMSource domSource = new DOMSource (doc); <BR><BR>//创建一个DOMResult对象,临时保存XSLT引擎的输出结果。<BR>DOMResult domResult = new DOMResult();<BR><BR>//下面调用JAXP中的XSLT引擎来实现输出DOM Tree中的数据到XML文件中的功能。<BR>//XSLT引擎的输入为DOMSource对象,输出为DOMResut对象。<BR>try <BR>{<BR>//首先创建一个TransformerFactory对象,再由此创建Transformer对象。Transformer<BR>//类相当于一个XSLT引擎。通常我们使用它来处理XSL文件,但是在这里我们使<BR>//用它来输出XML文档。<BR>TransformerFactory tf=TransformerFactory.newInstance(); <BR>Transformer t=tf.newTransformer (); <BR><BR>//设置XSLT引擎的属性(必不可少,否则会产生"汉字问题")。<BR>Properties properties = t.getOutputProperties(); <BR>properties.setProperty(OutputKeys.ENCODING,"GB2312");<BR>t.setOutputProperties(properties); <BR><BR>//关键的一步, 调用Transformer对象 (XSLT引擎)的transform()方法,该方法的第一<BR>//个参数是DOMSource对象,第二个参数是DOMResult对象。<BR>t.transform(domSource,domResult);<BR><BR>//创建缺省的Xalan XML Serializer,使用它将临时存放在DOMResult对象<BR>//(domResult)中的内容以输出流的形式输出到输出介质中。<BR>Serializer serializer = SerializerFactory.getSerializer<BR>(OutputProperties.getDefaultMethodProperties("xml"));<BR><BR>//设置Xalan XML Serializer的输出属性,这一步必不可少,否则也可能产生<BR>//所谓的"汉字问题"。<BR>Properties prop=serializer.getOutputFormat();<BR>prop.setProperty("encoding","GB2312");<BR>serializer.setOutputFormat(prop);<BR><BR>//创建一个File对象,代表DOM Tree所包含的数据的输出介质,这是一个XML文件。<BR>File f = new File ("xuser3.xml");<BR><BR>//创建文件输出流对象fos,请留意构造函数的参数。<BR>FileOutputStream fos=new FileOutputStream(f);<BR><BR>//设置Xalan XML Serializer的输出流。<BR>serializer.setOutputStream(fos);<BR><BR>//串行化输出结果。<BR>serializer.asDOMSerializer().serialize(domResult.getNode());<BR>}<BR>catch (Exception tce)<BR>{ <BR>tce.printStackTrace(); <BR>}</TD></TR></TBODY></TABLE><BR>　　这个方法不太常用,而且似乎有点画蛇添足,所以我们就不展开讨论了。完整的例子请参考下列文件: AddRecord4.java(见附件)、user.xml(见附件)。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行AddRecord4.java这个程序,你需要到网址<A href="http://xml.apache.org/dist/去下载安装Apache" target=_blank>http://xml.apache.org/dist/去下载安装Apache</A> Xalan和Apache Xerces。 <BR><BR>　　或者是到网址<A href="http://java.sun.com/xml/download.html去下载安装Java" target=_blank>http://java.sun.com/xml/download.html去下载安装Java</A> XML Pack。因为最新的Java XML Pack(Winter 01 版)包含了Apache Xalan和Apache Xerces技术在内。 <BR><BR>　　结论: <BR><BR>　　本文简略的讨论了Java语言编程中更新XML文档的四种方法。第一种方法是直接读写XML文件,这种方法十分繁琐,而且比较容易出错,极少使用,除非你需要开发自己的XML Parser,否则不会使用这种方法。第二种方法是使用Apache Crimson的XmlDocument类,这种方法极为简单,使用方便,如果你选用Apache Crimson作为XML解析器,那么不妨使用这种方法,不过这种方法似乎效率不高(源于效率低下的Apache Crimson),另外,高版本的JAXP或者是Java XML Pack、JWSDP不直接支持Apache Crimson,亦即这种方法不通用。第三种方法是使用JAXP的XSLT引擎(Transformer类)来输出XML文档,这种方法也许是标准的方法了,使用起来十分灵活,特别是可以自如控制输出格式,我们推荐采用这种方法。第四种方法是第三种方法的变种,采用了Xalan XML Serializer,引入了串行化操作,对于大量文档的修改/输出有优越性,可惜的是要重复设置XSLT引擎的属性和XML Serializer的输出属性,比较麻烦,而且依赖于Apache Xalan和Apache Xerces技术,通用性略显不足。 <BR><BR>　　除了上面讨论的四种方法以外,实际上应用别的API(比如JDOM、Castor、XML4J、Oracle XML Parser V2)也有很多办法可以更新XML文档,限于篇幅,在这里就不一一讨论了。&nbsp; </FONT></DIV><img src ="http://www.blogjava.net/Victor/aggbug/23756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-14 09:02 <a href="http://www.blogjava.net/Victor/articles/23756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用struts+spring+hibernate组装你的web应用架构</title><link>http://www.blogjava.net/Victor/articles/22589.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Mon, 05 Dec 2005 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/22589.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/22589.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/22589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/22589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/22589.html</trackback:ping><description><![CDATA[其实，就算用Java建造一个不是很烦琐的web应用，也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。 从高处看，摆在开发者面前有很多问题：要考虑是怎样建立用户接口？在哪里处理业务逻辑？ 怎样持久化的数据。 而这三层构架中，每一层都有他们要仔细考虑的。 各个层该使用什么技术？ 怎样的设计能松散耦合还能灵活改变？ 怎样替换某个层而不影响整体构架？应用程序如何做各种级别的业务处理（比如事务处理）？ <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 构架一个Web应用需要弄明白好多问题。 幸运的是，已经有不少开发者已经遇到过这类问题，并且建立了处理这类问题的框架。 一个好框架具备以下几点： 减轻开发者处理复杂的问题的负担（"不重复发明轮子"）； 内部有良好的扩展； 并且有一个支持它的强大的用户团体。 好的构架一般有针对性的处理某一类问题，并且能将它做好（Do One Thing well）。 然而，你的程序中有几个层可能需要使用特定的框架，已经完成的UI(用户接口) 并不代表你也可以把你的业务逻辑和持久逻辑偶合到你的UI部分。 举个例子， 你不该在一个Controller(控制器)里面写JDBC代码作为你的业务逻辑， 这不是控制器应该提供的。 一个UI 控制器应该委派给其它给在UI范围之外的轻量级组件。 好的框架应该能指导代码如何分布。 更重要的是，框架能把开发者从编码中解放出来，使他们能专心于应用程序的逻辑（这对客户来说很重要）。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这篇文章将讨论怎样结合几种著名的框架来使得你的应用程序做到松弛耦合。 <BR>如何建立你的架构，并且怎样让你的各个应用层保持一致。？如何整合框架以便让每个层在以一种松散偶合的方式彼此作用而不用管低层的技术细节？这对我们来说真是一种挑战。 这里讨论一个整合框架的策略( 使用3 种受欢迎的开源框架) ：表示层我们用Struts； 业务层我们用Spring；而持久层则用Hibernate。 你也可以用其他FrameWork替换只要能得到同样的效果。 见图1 （框架组合示意图） <BR><BR><IMG height=193 src="http://www.onjava.com/onjava/2004/04/07/graphics/wiring.gif" width=450 border=0> <BR><BR><SPAN style="FONT-WEIGHT: bold">应用程序的分层</SPAN> <BR><BR>大部分的Web应用在职责上至少能被分成4层。 这四层是：presentation（描述），persistence（持久），business（业务）和domain model（域模块）。每个层在处理程序上都应该有一项明确的责任, 而不应该在功能上与其它层混合，并且每个层要与其它层分开的，但要给他们之间放一个通信接口。 我们就从介绍各个层开始，讨论一下这些层应该提供什么，不应该提供什么。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">表示层(The Presentation Layer) </SPAN><BR><BR>一般来讲，一个典型的Web应用的的末端应该是表示层。 很多Java发者也理解Struts所提供的。 象业务逻辑之类的被打包到org.apache.struts.Action.， 因此，我们很赞成使用Struts这样的框架。 <BR><BR><BR><BR>下面是Struts所负责的： <BR><BR>* 管理用户的请求,做出相应的响应。 <BR><BR>* 提供一个Controller ,委派调用业务逻辑和其它上层处理。 <BR><BR>* 处理异常，抛给Struts Action <BR><BR>* 为显示提供一个模型 <BR><BR>* UI验证。 <BR><BR><BR><BR>以下条款，不该在Struts显示层的编码中经常出现。 它们与显示层无关的。 <BR><BR>* 直接的与数据库通信，例如JDBC调用。 <BR><BR>* 与你应用程序相关联的业务逻辑以及校验。 <BR><BR>* 事物管理。 <BR><BR>在表示层引入这些代码，则会带来高偶合和麻烦的维护。 <BR><BR><BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">持久层(The Persistence Layer)</SPAN> <BR><BR>典型的Web应用的另一个末端是持久层。这里通常是程序最容易失控的地方。开发者总是低估构建他们自己的持久框架的挑战性。系统内部的持续层不但需要大量调试时间，而且还经常缺少功能使之变得难以控制，这是持久层的通病。 还好有几个ORM开源框架很好的解决了这类问题。尤其是Hibernate。 Hibernate为java提供了OR持久化机制和查询服务, 它还给已经熟悉SQL和JDBC API 的Java开发者一个学习桥梁，他们学习起来很方便。 Hibernate的持久对象是基于POJO和Java collections。此外，使用Hibernate并不妨碍你正在使用的IDE。 <BR><BR><BR><BR>请看下面的条目，你在持久层编码中需要了解的。 <BR><BR>* 查询对象的相关信息的语句。 Hibernate通过一个OO查询语言（HQL）或者正则表达的API来完成查询。 HQL非常类似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。 你需要学习一些新的HQL语言； 不管怎样，他们容易理解而文档也做的很好。 HQL是一种对象查询的自然语言，花很小的代价就能学习它。 <BR><BR>* 如何存储，更新，删除数据库记录。 <BR><BR>* 象Hibernate这类的高级ORM框架支持大部分主流数据库，并且他们支持 Parent/child关系，事物处理，继承和多态。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">业务层（The Business Layer）</SPAN> <BR><BR>一个典型Web应用的中间部分是业务层或者服务层。 从编码的视角来看，这层是最容易被忽视的一层。 而我们却往往在UI层或持久层周围看到这些业务处理的代码，这其实是不正确的，因为它导致了程序代码的紧密偶合，这样一来，随着时间推移这些代码很难维护。幸好，针对这一问题有好几种Frameworks存在。 最受欢迎的两个框架是Spring和PicoContainer。 这些为也被称为microcontainers，他们能让你很好的把对象搭配起来。 这两个框架都着手于‘依赖注射'(dependency injection)(还有我们知道的‘控制反转'Inversion of Control=IoC)这样的简单概念。 这篇文章将关注于Spring的注射（译注：通过一个给定参数的Setter方法来构造Bean,有所不同于Factory）, Spring还提供了Setter Injection(type2)，Constructor Injection(type3)等方式供我们选择。 Spring把程序中所涉及到包含业务逻辑和Dao的Objects——例如transaction management handler（事物管理控制）、Object Factoris(对象工厂)、service objects（服务组件）——都通过XML来配置联系起来。 <BR><BR><BR><BR>后面我们会举个例子来揭示一下Spring 是怎样运用这些概念。 <BR><BR>业务层所负责的如下： <BR><BR>* 处理应用程序的 业务逻辑和业务校验 <BR><BR>* 管理事物 <BR><BR>* 允许与其它层相互作用的接口 <BR><BR>* 管理业务层级别的对象的依赖。 <BR><BR>* 在显示层和持久层之间增加了一个灵活的机制，使得他们不直接的联系在一起。 <BR><BR>* 通过揭示 从显示层到业务层之间的Context来得到business services。 <BR><BR>* 管理程序的执行（从业务层到持久层）。 <BR><BR><BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">域模块层（The Domain Model Layer ）</SPAN> <BR>既然我们致力于的是一个不是很复杂的Web的应用， 我们需要一个对象集合，让它在不同层之间移动的。 域模块层由实际需求中的业务对象组成 比如, OrderLineItem , Product等等。 开发者在这层 不用管那些DTOs，仅关注domain object即可。 例如，Hibernate允许你将数据库中的信息存放入对象（domain objects），这样你可以在连接断开的情况下把这些数据显示到UI层。 而那些对象也可以返回给持续层，从而在数据库里更新。 而且，你不必把对象转化成DTOs（这可能似的它在不同层之间的在传输过程中丢失），这个模型使得Java开发者能很自然运用OO，而不需要附加的编码。 <BR><BR>一个简单例子 <BR><BR><BR><BR>既然我们已经从全局上理解这些组件。 现在就让我们开始实践吧。 我们还是用 Struts，Spring 和Hibernate。这三个框架已经被描述够多了，这里就不重复介绍了。 这篇文章举例指导你如何使用这三个框架整合开发, 并向你揭示 一个请求是如何贯穿于各个层的。（从用户的加入一个Order到数据库，显示；进而更新、删除）。 <BR><BR><BR><BR>从这里可以下载到程序程序原代码（<A href="http://www.onjava.com/onjava/2004/04/07/examples/wiring.zip" target=_blank rel=nofollow><FONT color=#0000ff>download</FONT></A>） <BR><BR>既然每个层是互相作用的，我们就先来创建domain objects。首先，我们要在这些Object中要确定那些是需要持久化的，哪些是提供给business logic，那些是显示接口的设计。 下一步，我们将配置我们的持久层并且定义好Hibernate的OR mappings。然后定义好Business Objects。有了这些组成部分之后，我们将 使用Spring把这些连接起来。 最后，我们提供给Spring一个持久层，从这个持久层里我们可以知道它是如何与业务逻辑层（business service layer）通信的，以及它是怎样处理其他层抛出的异常的。。 <BR><BR><BR><BR><SPAN style="FONT-WEIGHT: bold">域对象层（Domain Object Layer）</SPAN> <BR><BR><BR>这层是编码的着手点，我们的编码就从这层开始。 例子中Order 与OrderItem 是一个One—To—Many的关系。 下面就是Domain Object Layer的两个对象： <BR><BR><BR><BR>· com.meagle.bo.Order.java: 包含了一个Order的概要信息 <BR><BR>· com.meagle.bo.OrderLineItem.java: 包含了Order的详细信息 <BR><BR>好好考虑怎你的package命名,这反应出了你是怎样分层的。 例如 domain objects在程序中可能打包在com.meagle.bo内。 更详细一点将打包在com. meagle.bo的子目录下面。business logic应该从com.meagle.serice开始打包，而DAO 对象应该位于com.meagle.service.dao.hibernate。反应Forms和Actions的 持久对象（presentation classes） 应该分别放在 com.meagle.action和com.meagle.forms包。 准确的给包命名使得你的classes很好分割并且易于维护，并且在你添加新的classes时，能使得程序结构上保持上下一致。 <BR><BR><SPAN style="FONT-WEIGHT: bold">持久层的配置（Persistence Layer Configuration）</SPAN> <BR><BR>建立Hibernate的持久层 需要好几个步骤。 第一步让我们把BO持久化。 既然Hibernate是通过POJO工作的， 因此Order和 OrderLineItem对象需要给所有的fileds 加上getter,setter方法。 Hibernate通过XML文件来映射(OR)对象，以下两个xml文件分别映射了Order 和OrderItem对象。（这里有个叫XDoclet工具可以自动生成你的XML影射文件） <BR><BR>- Order.hbm.xml <BR>- OrderLineItem.hbm.xml <BR><BR>你可以在WebContent/WEB-INF/classes/com/meagle/bo目录下找到这些xml文件。Hibernate的 [urlhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html]SessionFactory [/url]是用来告诉程序 应该与哪个数据库通信，该使用哪个连接池或使用了DataSource， 应该加载哪些持久对象。而<A href="http://www.hibernate.org/hib_docs/api/net/sf/hibernate/Session.html" target=_blank rel=nofollow><FONT color=#0000ff>Session</FONT></A>接口是用来完成Selecting，Saving，Delete和Updating这些操作。 后面的我们将讲述SessionFactory和Session是怎样设置的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">业务层的配置（Business Layer Configuration）</SPAN> <BR><BR>既然我们已经有了domain objects，接下来我们就要business service objects了，用他们来执行程序的logic,调用持久层，得到UI层的requests,处理transactions，并且控制exceptions。 为了将这些连接起来并且易于管理，我们将使用面向方面的 SpringFramework。 Spring 提供了 控制倒置（inversion of control 0==IoC)和注射依赖设置（setter dependency injection）这些方式（可供选择），用XML文件将对象连接起来。 IoC是一个简单概念（它允许一个对象在上层接受其他对象的创建），用IoC这种方式让你的对象从创建中释放了出来，降低了偶合度。 <BR><BR><BR><BR><BR>这里是一个没有使用IoC的对象创建的例子，它有很高偶合度。 <BR><BR><BR><IMG height=212 src="http://www.onjava.com/onjava/2004/04/07/graphics/nonioc.gif" width=250 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 2.没有使用 IoC. A 创建了 B 和 C</SPAN> <BR><BR>而这里是一个使用IoC的例子，这种方式允许对象在高层可以创建并进入另外一个对象，所以这样可以直接被执行。 <BR><IMG height=210 src="http://www.onjava.com/onjava/2004/04/07/graphics/ioc.gif" width=250 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 3. 对象使用了 IoC。 A 包含了接受B,C的 setter方法 , 这同样达到了 由A创建B,C的目的。</SPAN> <BR><BR>建立我们的业务服务对象（Building Our Business Service Objects） <BR><BR><BR>Business Object中的Setter方法接受的是接口，这样我们可以很松散的定义对象实现，然后注入。 在我们的案例中，我们将用一个business service object接收一个DAO,用它来控制domain objects的持久化。 由于在这个例子中使用了Hibernate，我们可以很方便的用其他持久框架实现 同时通知Spring 有新的DAO可以使用了。 <BR><BR>在面向接口的编程中，你会明白 "注射依赖"模式是怎样松散耦合你的业务逻辑和持久机制的：）。 <BR><BR><BR><BR>下面是一个接口business service object，DAO代码片段： <BR><BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>public interface IOrderService { <BR><BR>&nbsp; public abstract Order saveNewOrder(Order order) <BR><BR>&nbsp; &nbsp; throws OrderException, <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;OrderMinimumAmountException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract List findOrderByUser( <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String user) <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throws OrderException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract Order findOrderById(int id) <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throws OrderException; <BR><BR>&nbsp; <BR><BR>&nbsp; public abstract void setOrderDAO( <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IOrderDAO orderDAO); <BR><BR>} <BR><BR>&nbsp; </TD></TR></TBODY></TABLE><BR>注意到这段代码里有一个 setOrderDao（），它就是一个DAO Object设置方法（注射器）。 但这里并没有一个getOrderDao的方法，这不必要，因为你并不会在外部访问这个orderDao。这个DAO Objecte将被调用，和我们的persistence layer 通信。我们将用Spring把DAO Object 和 business service object搭配起来的。因为我们是面向接口编程的，所以并不需要将实现类紧密的耦合在一起。 <BR><BR><BR><BR>接下去我们开始我们的DAO的实现类进行编码。 既然Spring已经有对Hibernate的支持，那这个例子就直接继承<A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/support/HibernateDaoSupport.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateDaoSupport</FONT></A>类了，这个类很有用，我们可以参考<A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTemplate.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateTemplate</FONT></A>（它主要是针对HibernateDaoSupport的一个用法，译注：具体可以查看<A href="http://www.springframework.org/docs/api/index.html" target=_blank rel=nofollow><FONT color=#0000ff>Srping 的API</FONT></A>）。 下面是这个DAO接口代码： <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>public interface IOrderDAO { <BR>&nbsp; public abstract Order findOrderById( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final int id); <BR>&nbsp; <BR>&nbsp; public abstract List findOrdersPlaceByUser( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;final String placedBy); <BR>&nbsp; public abstract Order saveOrder( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;final Order order); <BR>} </TD></TR></TBODY></TABLE><BR><BR>我们仍然要给我们持久层组装很多关联的对象，这里包含了HibernateSessionFactory 和TransactionManager。 Spring 提供了一个 <A href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTransactionManager.html" target=_blank rel=nofollow><FONT color=#0000ff>HibernateTransactionManager</FONT></A>，他用线程捆绑了一个Hibernate Session，用它来支持transactions(请查看<A href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.html" target=_blank rel=nofollow><FONT color=#0000ff>ThreadLocal</FONT></A>) 。 <BR><BR>下面是HibernateSessionFactory 和 HibernateTransactionManager:的配置： <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>&lt;bean id="mySessionFactory" <BR>&nbsp; &nbsp; &nbsp; &nbsp;class="org.springframework.orm.hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LocalSessionFactoryBean"&gt; <BR>&nbsp; &lt;property name="mappingResources"&gt; <BR>&nbsp; &nbsp; &lt;list&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;value&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; com/meagle/bo/Order.hbm.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/value&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;value&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; com/meagle/bo/OrderLineItem.hbm.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/value&gt; <BR>&nbsp; &nbsp; &lt;/list&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="hibernateProperties"&gt; <BR>&nbsp; &nbsp; &lt;props&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.dialect"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; net.sf.hibernate.dialect.MySQLDialect <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.show_sql"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; false <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.proxool.xml"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; C:/MyWebApps/.../WEB-INF/proxool.xml <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="hibernate.proxool.pool_alias"&gt; <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spring <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &lt;/props&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- Transaction manager for a single Hibernate <BR>SessionFactory (alternative to JTA) --&gt; <BR>&lt;bean id="myTransactionManager" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="org. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; springframework. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; orm. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HibernateTransactionManager"&gt; <BR>&nbsp; &lt;property name="sessionFactory"&gt; <BR>&nbsp; &nbsp; &lt;ref local="mySessionFactory"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;/bean&gt; </TD></TR></TBODY></TABLE><BR><BR><BR>可以看出：每个对象都可以在Spring 配置信息中用&lt;bean&gt;标签引用。在这里，mySessionFactory引用了HibernateSessionFactory，而myTransactionManager引用了HibernateTransactionManage。 注意代码中myTransactionManger Bean有个sessionFactory属性。 HibernateTransactionManager有个sessionFactory setter 和 getter方法，这是用来在Spring启动的时候实现"依赖注入" （dependency injection）的。 在sessionFactory 属性里 引用mySessionFactory。这两个对象在Spring容器初始化后就被组装了起来了。 这样的搭配让你从 单例（singleton objects）和工厂（factories）中解放了出来，降低了代码的维护代价。 mySessionFactory.的两个属性，分别是用来注入mappingResources 和 hibernatePropertes的。通常，如果你在Spring之外使用Hibernate,这样的设置应该放在hibernate.cfg.xml中的。 不管怎样,Spring提供了一个便捷的方式-----在Spring内部配置中并入了Hibernate的配置。 如果要得到更多的信息，可以查阅Spring API。 <BR><BR><BR><BR><BR><BR>既然我们已经组装配置好了Service Beans，就需要把Business Service Object和 DAO也组装起来，并把这些对象配到一个事务管理器（transaction manager）里。 <BR><BR><BR><BR>在Spring中的配置信息： <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>&lt;!-- ORDER SERVICE --&gt; <BR>&lt;bean id="orderService" <BR>&nbsp; class="org. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;springframework. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;transaction. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;interceptor. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TransactionProxyFactoryBean"&gt; <BR>&nbsp; &lt;property name="transactionManager"&gt; <BR>&nbsp; &nbsp; &lt;ref local="myTransactionManager"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="target"&gt; <BR>&nbsp; &nbsp; &lt;ref local="orderTarget"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&nbsp; &lt;property name="transactionAttributes"&gt; <BR>&nbsp; &nbsp; &lt;props&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="find*"&gt; <BR>&nbsp; &nbsp; &nbsp;PROPAGATION_REQUIRED,readOnly,-OrderException <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &nbsp; &lt;prop key="save*"&gt; <BR>&nbsp; &nbsp; &nbsp;PROPAGATION_REQUIRED,-OrderException <BR>&nbsp; &nbsp; &nbsp; &lt;/prop&gt; <BR>&nbsp; &nbsp; &lt;/props&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- ORDER TARGET PRIMARY BUSINESS OBJECT: <BR>Hibernate implementation --&gt; <BR>&lt;bean id="orderTarget" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spring. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderServiceSpringImpl"&gt; <BR>&nbsp; &lt;property name="orderDAO"&gt; <BR>&nbsp; &nbsp; &lt;ref local="orderDAO"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; <BR>&nbsp; <BR>&lt;!-- ORDER DAO OBJECT --&gt; <BR>&lt;bean id="orderDAO" <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;class="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dao. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hibernate. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderHibernateDAO"&gt; <BR>&nbsp; &lt;property name="sessionFactory"&gt; <BR>&nbsp; &nbsp; &lt;ref local="mySessionFactory"/&gt; <BR>&nbsp; &lt;/property&gt; <BR>&lt;/bean&gt; </TD></TR></TBODY></TABLE><BR><BR><BR><BR>图4 是我们对象搭建的一个提纲。 从中可以看出，每个对象都联系着Spring，并且能通过Spring注入到其他对象。把它与Spring的配置文件比较，观察他们之间的关系 <BR><BR><IMG height=352 src="http://www.onjava.com/onjava/2004/04/07/graphics/spring_wiring.gif" width=436 border=0> <BR><BR><SPAN style="FONT-STYLE: italic">图 4. Spring就是这样基于配置文件，将各个Bean搭建在一起。</SPAN> <BR><BR>这个例子使用一个TransactionProxyFactoryBean，它定义了一个setTransactionManager()。 这对象很有用，他能很方便的处理你申明的事物还有Service Object。 你可以通过transactionAttributes属性来定义怎样处理。 想知道更多还是参考TransactionAttributeEditor吧。 <BR><BR>TransactionProxyFactoryBean 还有个setter. 这会被我们 Business service object（orderTarget）引用， orderTarget定义了 业务服务层，并且它还有个属性，由setOrderDAO()引用。这个属性 <BR><BR><BR><BR>Spring 和Bean 的还有一点要注意的： bean可以以用两种方式创造。 这些都在单例模式（Sington）和原型模式（propotype）中定义了。 默认的方式是singleton,这意味着共享的实例将被束缚。 而原形模式是在Spring用到bean的时候允许新建实例的。当每个用户需要得到他们自己Bean的Copy时，你应该仅使用prototype模式。（更多的请参考设计模式中的单例模式和原形模式） <BR><BR><SPAN style="FONT-WEIGHT: bold">提供一个服务定位器（Providing a Service Locator）</SPAN> <BR>既然我们已经将我们的Serices和DAO搭配起来了。我们需要把我们的Service显示到其他层。 这个通常是在Struts或者Swing这层里编码。一个简单方法就是用 服务定位器返回给Spring context 。当然，可以通过直接调用Spring中的Bean来做。 <BR><BR>下面是一个Struts Actin 中的服务定位器的一个例子。 <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>public abstract class BaseAction extends Action { <BR>&nbsp; <BR>&nbsp; private IOrderService orderService; <BR>&nbsp; <BR>&nbsp; public void setServlet(ActionServlet <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;actionServlet) { <BR>&nbsp; &nbsp; super.setServlet(actionServlet); <BR>&nbsp; &nbsp; ServletContext servletContext = <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;actionServlet.getServletContext(); <BR>&nbsp; <BR>&nbsp; &nbsp; WebApplicationContext wac = <BR>&nbsp; &nbsp; &nbsp; WebApplicationContextUtils. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;getRequiredWebApplicationContext( <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;servletContext); <BR>&nbsp; <BR>&nbsp; &nbsp; &nbsp; this.orderService = (IOrderService) <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;wac.getBean("orderService"); <BR>&nbsp; } <BR>&nbsp; <BR>&nbsp; protected IOrderService getOrderService() { <BR>&nbsp; &nbsp; return orderService; <BR>&nbsp; } <BR>} <BR>&nbsp; </TD></TR></TBODY></TABLE><BR><SPAN style="FONT-WEIGHT: bold">UI 层配置 （UI Layer Configuration）</SPAN> <BR><BR>这个例子里UI层 使用了Struts framework. 这里我们要讲述一下在给程序分层的时候， 哪些是和Struts部分的。我们就从一个Struts-config.xml文件中的Action的配置信息开始吧。 <BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD><BR>struts-config.xml file. <BR><BR>&lt;action path="/SaveNewOrder" <BR>&nbsp; &nbsp; type="com.meagle.action.SaveOrderAction" <BR>&nbsp; &nbsp; name="OrderForm" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; validate="true" <BR>&nbsp; &nbsp; input="/NewOrder.jsp"&gt; <BR>&nbsp; &lt;display-name&gt;Save New Order&lt;/display-name&gt; <BR>&nbsp; &lt;exception key="error.order.save" <BR>&nbsp; &nbsp; path="/NewOrder.jsp" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; type="com.meagle.exception.OrderException"/&gt; <BR>&nbsp; &lt;exception key="error.order.not.enough.money" <BR>&nbsp; &nbsp; path="/NewOrder.jsp" <BR>&nbsp; &nbsp; scope="request" <BR>&nbsp; &nbsp; type="com. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; meagle. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exception. <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OrderMinimumAmountException"/&gt; <BR>&nbsp; &lt;forward name="success" path="/ViewOrder.jsp"/&gt; <BR>&nbsp; &lt;forward name="failure" path="/NewOrder.jsp"/&gt; <BR>&lt;/action&gt; </TD></TR></TBODY></TABLE><BR>SaveNewOrder 这个Action是用来持久化UI层里的表单提交过来Order的。这是Struts中一个很典型的Action; 注意观察这个Action中exception配置，这些Exceptions也在Spring 配置文件(applicationContext-hibernate.xml)中配置了（就在 business service object 的transactionAttributes属性里）。 当异常在业务层被被抛出时，我们可以控制他们，并适当的显示给UI层。 <BR><BR>第一个异常，OrderException,在持久层保存order对象失败的时候被触发。这将导致事物回滚并且通过BO把异常回传到Struts这一层。 <BR><BR>第二个异常，OrderMinimumAmountException也同第一个一样。 <BR><BR><BR><BR><BR><BR>搭配整和的最后一步 通过是让你显示层和业务层相结合。这个已经被服务定位器（service locator）实现了（前面讨论过了）， 这里服务层作为一个接口提供给我们的业务逻辑和持久层。 <BR><BR><BR><BR>SaveNewOrder Action 在Struts中用一个服务定位器（service locator）来调用执行业务方法的。 方法代码如下： <BR><BR><BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>代码:</STRONG> </TD></TR>
<TR>
<TD>public ActionForward execute( <BR><BR>&nbsp; ActionMapping mapping, <BR><BR>&nbsp; ActionForm form, <BR><BR>&nbsp; javax.servlet.http.HttpServletRequest request, <BR><BR>&nbsp; javax.servlet.http.HttpServletResponse response) <BR><BR>&nbsp; throws java.lang.Exception { <BR><BR>&nbsp; <BR><BR>&nbsp; OrderForm oForm = (OrderForm) form; <BR><BR>&nbsp; <BR><BR>&nbsp; // Use the form to build an Order object that <BR><BR>&nbsp; // can be saved in the persistence layer. <BR><BR>&nbsp; // See the full source code in the sample app. <BR><BR>&nbsp; <BR><BR>&nbsp; // Obtain the wired business service object <BR><BR>&nbsp; // from the service locator configuration <BR><BR>&nbsp; // in BaseAction. <BR><BR>&nbsp; // Delegate the save to the service layer and <BR><BR>&nbsp; // further upstream to save the Order object. <BR><BR>&nbsp; getOrderService().saveNewOrder(order); <BR><BR>&nbsp; <BR><BR>&nbsp; oForm.setOrder(order); <BR><BR>&nbsp; <BR><BR>&nbsp; ActionMessages messages = new ActionMessages(); <BR><BR>&nbsp; messages.add( <BR><BR>&nbsp; &nbsp; &nbsp; ActionMessages.GLOBAL_MESSAGE, <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new ActionMessage( <BR><BR>&nbsp; &nbsp; &nbsp; "message.order.saved.successfully")); <BR><BR>&nbsp; <BR><BR>&nbsp; saveMessages(request, messages); <BR><BR>&nbsp; <BR><BR>&nbsp; return mapping.findForward("success"); <BR><BR>} </TD></TR></TBODY></TABLE><BR><BR>总结 <BR><BR>这篇文章在技术和构架方面掩盖了很多低层的基础信息， 文章的主要的意图在于让你意识到如何给你应用程序分层。 分层可以"解耦"你的代码——允许新的组件被添加进来，而且让你的代码易于维护。 这里用到的技术只是专注于把"解偶"做好。 不管怎样，使用这样的构架可以让你用其他技术代替现在的层。 例如，你可能不使用Hibernate实现持久化。既然你在DAO中面向接口的编程的，所以你完全可以用iBATIS来代替。或者，你也可能想用Struts外的其他的技术或者框架替换现在的UI层（转换久层，实现层并不应该直接影响到你的业务逻辑和业务服务层）。 用适当的框架搭建你的Web应用，其实也不是一件烦琐的工作，更主要的是它"解耦"了你程序中的各个层。 <BR><BR><BR><BR><BR><BR>后记： <BR><BR>看完这篇文章后，只是觉得很喜欢，于是就翻译了，当然同时也准备着挨大家扔来的鸡蛋：）。 <BR><BR>这篇文章里并没有太多的技术细节，和详细的步骤。如果你从未使用过这些框架而在运行实例程序遇上困难的话，可以到CSDN论坛Java Open Source版发贴，我一定会详细解答的（啊哦，这不算做广告吧？）， <BR><BR>文章是从一个构架的角度讲述了如何搭配现有的开源框架进行分层， 有太多的术语我都不知道怎么表达，而且可能有很多语句存在错误。如果影响了你的阅读，请你直接点原文地址，我同时也象你说声抱歉。 <BR><BR><BR><BR>作者简介：Mark Eagle 高级软件工程师，亚特兰大。 <BR>翻 译：Totodo,软件工程师 <BR><BR><BR><BR><BR><BR>参考： <BR><BR>Struts：http://jakarta.apache.org/struts/index.html <BR><BR>Spring: <A href="http://www.springframework.org/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.springframework.org</FONT></A> <BR><BR>Hibernate: <A href="http://www.hibernate.org/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.hibernate.org</FONT></A> <BR><BR><A href="http://www.hibernate.org.cn/" target=_blank rel=nofollow><FONT color=#0000ff>http://www.hibernate.org.cn</FONT></A> <BR><BR>关于控制反转IOC和依赖注射：http://www.martinfowler.com/articles/injection.html <BR><BR>原文: <BR><BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3</FONT></A> <BR><A href="http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4" target=_blank rel=nofollow><FONT color=#0000ff>http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4</FONT></A> <BR><img src ="http://www.blogjava.net/Victor/aggbug/22589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-05 15:26 <a href="http://www.blogjava.net/Victor/articles/22589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]J2EE架构的6个最佳实践</title><link>http://www.blogjava.net/Victor/articles/22587.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Mon, 05 Dec 2005 07:25:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/22587.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/22587.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/22587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/22587.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/22587.html</trackback:ping><description><![CDATA[虽然许多文章曾经讨论过J2EE最佳实践。那么，为什么我还要再写一篇文章呢？本文究竟与以前的文章有何不同或者说比其他文章好在哪呢？ <BR>　　首先，本文的目标读者是正在从事技术工作的架构师。为了避免浪费大家的才智，我会避免讲述一些陈腐的最佳实践，例如"日常构建（build&nbsp;daily）"、"测试一切（test&nbsp;everything）"和"经常集成（&nbsp;integrate&nbsp;often）。&nbsp;任何具有称职架构师的项目都有分工明确的、定义良好的团队结构。他们还为进行编码检查、构建代码（每日或在需要时）、进行测试（单元、集成和系统的）、部署和配置/释放管理而具备已记录的过程。 <BR>　　其次，我将跳过通常吹捧的最佳实践，例如"基于接口的设计"、"使用著名的设计模型"以及"使用面向服务的架构"等。相反，我将集中讲述我曾学过并且使用了若干年的6（不是很多）个方面的in-the-trench课程。最后，本文的目的是让您思考一下自己的架构，提供工作代码示例或者解决方案超出了本文的范围。下面就让我介绍一下这6课： <BR><BR>第1课：切勿绕过服务器端验证 <BR>　　作为一位软件顾问，我曾有机会不但设计并实现了Web应用程序，而且还评估/审核了许多Web应用程序。在复杂的、并且用JavaScript客户端封装的应用程序内，我经常遇到对用户输入信息执行大量检查的Web页面。即使HTML元素具有数据有效性的属性也如此，例如MAXLENGTH。只有在成功验证所有输入信息后，才能提交HTML表单。结果，一旦服务器端收到通知表单（请求），便恰当地执行业务逻辑。 <BR>　　在此，您发现问题了么？开发人员已经做了许多重要的假设。例如，他们假设所有的Web应用程序用户都同样诚实。开发人员还假设所有用户将总是使用他们测试过的浏览器访问Web应用程序。还有很多其他的假设。这些开发人员忘记了利用可以免费得到的工具，通过命令行很容易地模拟类似浏览器的行为。事实上，通过在浏览器窗口中键入适当的URL，您可以发送任何"posted"表单，尽管如此，通过禁用这些页面的GET请求，您很容易地阻止这样的"表单发送"。但是，您不能阻止人们模拟甚至创建他们自己的浏览器来入侵您的系统。 <BR>根本的问题在于开发人员不能确定客户端验证与服务器端验证的主要差别。两者的主要差别不在于验证究竟发生在哪里，例如在客户端或者在服务器端。主要的差别在于验证背后的目的不同。 <BR>　　客户端验证仅仅是方便。执行它可为用户提供快速反馈??使应用程序似乎做出响应，给人一种运行桌面应用程序的错觉。 <BR>　　另一方面，服务器端验证是构建安全Web应用程序必需的。不管在客户端一侧输入的是什么，它可以确保客户端送往服务器的所有数据都是有效的。 <BR>　　因而，只有服务器端验证才可以提供真正应用程序级的安全。许多开发人员陷入了错误感觉的圈套：只有在客户端进行所有数据的验证才能确保安全。下面是说明此观点的一个常见的示例： <BR>　　一个典型的登录页面拥有一个用来输入用户名的文本框和一个输入密码的文本框。在服务器端，某人在接收servlet中可能遇到一些代码，这些代码构成了下面形式的SQL查询： <BR>"SELECT&nbsp;*&nbsp;FROM&nbsp;SecurityTable&nbsp;WHERE&nbsp;username&nbsp;=&nbsp;'"&nbsp;+&nbsp;form.getParameter("username")&nbsp;+&nbsp;"'&nbsp;AND&nbsp;password&nbsp;=&nbsp;'"&nbsp;+&nbsp;form.getParameter("password")&nbsp;+&nbsp;"';"，并执行这些代码。如果查询在结果集的某一行返回，则用户登录成功，否则用户登录失败。 <BR>　　第一个问题是构造SQL的方式，但现在让我们暂时忽略它。如果用户在用户名中输入"Alice'--"会怎样呢？假设名为"Alice"的用户已经在SecurityTable中，这时此用户（更恰当的说法是黑客）成功地登录。我将把找出为什么会出现这种情况的原因做为留给您的一道习题。 <BR>　　许多创造性的客户端验证可以阻止一般的用户从浏览器中这样登录。但对于已经禁用了JavaScript的客户端，或者那些能够使用其他类似浏览器程序直接发送命令（HTTP&nbsp;POST和GET命令）的高级用户（或者说黑客）来说，我们又有什么办法呢？服务器端验证是防止这种漏洞类型所必须的。这时，SSL、防火墙等都派不上用场了。 <BR><BR>第2课：安全并非是附加物 <BR>　　如第1课所述，我曾有幸研究过许多Web应用程序。我发现所有的JavaServer&nbsp;Page（JSP）都有一个共同的主题，那就是具有类似下面伪代码的布局： <BR><BR>&lt;% <BR>User&nbsp;user&nbsp;=&nbsp; <BR>session.getAttribute("User"); <BR>if(user&nbsp;==&nbsp;null) <BR>{ <BR>//&nbsp;redirect&nbsp;to&nbsp; <BR>//&nbsp;the&nbsp;logon&nbsp;page... <BR>}&nbsp; <BR>if(!user.role.equals("manager")) <BR>{ <BR>//&nbsp;redirect&nbsp;to&nbsp;the <BR>//&nbsp;"unauthorized"&nbsp;page... <BR>} <BR>%&gt; <BR><BR>&lt;!- <BR>HTML,&nbsp;JavaScript,&nbsp;and&nbsp;JSP <BR>code&nbsp;to&nbsp;display&nbsp;data&nbsp;and <BR>allow&nbsp;user&nbsp;interaction&nbsp;--&gt; <BR><BR>　　如果项目使用诸如Struts这样的MVC框架，所有的Action&nbsp;Bean都会具有类似的代码。尽管最后这些代码可能运行得很好，但如果您发现一个bug，或者您必须添加一个新的角色（例如，"guest"或者"admin"），这就会代表一场维护恶梦。 <BR>　　此外，所有的开发人员，不管您多年轻，都需要熟悉这种编码模式。当然，您可以用一些JSP标签来整理JSP代码，可以创建一个清除派生Action&nbsp;Bean的基本Action&nbsp;Bean。尽管如此，由于与安全相关的代码会分布到多个地方，所以维护时的恶梦仍旧存在。由于Web应用程序的安全是强迫建立在应用程序代码的级别上（由多个开发人员），而不是建立在架构级别上，所以Web应用程序还是很可能存在弱点。 <BR>　　很可能，根本的问题是在项目接近完成时才处理安全性问题。最近作为一名架构师，我曾在一年多的时间里亲历了某一要实现项目的6个版本，而直到第四版时我们才提到了安全性??即使该项目会将高度敏感的个人数据暴露于Web上，我们也没有注意到安全性。为了更改发布计划，我们卷入了与项目资助人及其管理人员的争斗中，以便在第一版中包含所有与安全相关的功能，并将一些"业务"功能放在后续的版本中。最终，我们赢得了胜利。而且由于应用程序的安全性相当高，能够保护客户的私有数据，这一点我们引以为荣，我们的客户也非常高兴。 <BR>　　遗憾的是，在大多数应用程序中，安全性看起来并未增加任何实际的商业价值，所以直到最后才解决。发生这种情况时，人们才匆忙开发与安全相关的代码，而丝毫没有考虑解决方案的长期可维护性或者健壮性。忽视该安全性的另一个征兆是缺乏全面的服务器端验证，如我在第1课中所述，这一点是安全Web应用程序的一个重要组成部分。 <BR>　　记住：J2EE&nbsp;Web应用程序的安全性并非仅仅是在Web.xml&nbsp;和ejb-jar.xml文件中使用合适的声明，也不是使用J2EE技术，如Java&nbsp;认证和授权服务（Java&nbsp;Authentication&nbsp;and&nbsp;Authorization&nbsp;Service，JAAS）。而是经过深思熟虑后的设计，且实现一个支持它的架构。 <BR><BR>第3课：国际化（I18N）不再是纸上谈兵&nbsp; <BR>　　当今世界的事实是许多英语非母语的人们将访问您的公共Web应用程序。随着电子政务的实行，由于它允许人们（某个国家的居民）在线与政府机构交互，所以这一点特别真实。这样的例子包括换发驾照或者车辆登记证。许多第一语言不是英语的人们很可能将访问这样的应用程序。国际化（即："i18n"，因为在"internationalization"这个单词中，字母i和字母n之间一共有18个字母）使得您的应用程序能够支持多种语言。 <BR>　　显然，如果您的JSP&nbsp;页面中有硬编码的文本，或者您的Java代码返回硬编码的错误消息，那么您要花费很多时间开发此Web应用程序的西班牙语版本。然而，在Web应用程序中，为了支持多种语言，文本不是惟一必须"具体化"的部分。因为许多图像中嵌有文字，所以图形和图像也应该是可配置的。在极端的情况下，图像（或者颜色）在不同的文化背景中可能有完全不同的意思。类似地，任何格式化数字和日期的Java代码也必须本地化。但问题是：您的页面布局可能也需要更改。 <BR>　　例如，如果您使用HTML表格来格式化和显示菜单选项、应用程序题头或注脚，则您可能必须为每一种支持的语言更改每一栏的最小宽度和表格其他可能的方面。为了适应不同的字体和颜色，您可能必须为每一种语言使用单独的样式表。 <BR>　　显然，现在创建一个国际化的Web应用程序面临的是架构挑战而不是应用程序方面的挑战。一个架构良好的Web应用程序意味着您的JSP页面和所有与业务相关的（应用程序特有的）Java代码都不知不觉地选择了本地化。要记住的教训是：不要因为Java、J2EE支持国际化而不考虑国际化。您必须从第一天起就记住设计具有国际化的解决方案。 <BR><BR>第4课：在MVC表示中避免共同的错误&nbsp; <BR>　　J2EE开发已经足够成熟，在表示层，大多数项目使用MVC架构的某些形式，例如Struts。在这样的项目中，我常见到的现象是对MVC模式的误用。下面是几个示例。 <BR>　　常见的误用是在模型层（例如，在Struts的Action&nbsp;Bean中）实现了所有的业务逻辑。不要忘了，表示层的模型层仍然是表示层的一部分。使用该模型层的正确方法是调用适当的业务层服务（或对象）并将结果发送到视图层（view&nbsp;layer）。用设计模式术语来说，MVC表示层的模型应该作为业务层的外观（Fa?ade）来实现。更好的方法是，使用核心J2EE模式（Core&nbsp;J2EE&nbsp;Patterns）中论述到的Business&nbsp;Delegate模式。这段自书中摘录的内容精彩地概述了将您的模型作为Business&nbsp;Delegate来实现的要点和优点： <BR>　　Business&nbsp;Delegate起到客户端业务抽象化的作用。它抽象化，进而隐藏业务服务的实现。使用Business&nbsp;Delegate，可以降低表示层客户端和系统的业务服务.之间的耦合程度。根据实现策略不同，Business&nbsp;Delegate可以在业务服务API的实现中，保护客户端不受可能的变动性影响。这样，在业务服务API或其底层实现变化时，可以潜在地减少必须修改表示层客户端代码的次数。 <BR>　　另一个常见的错误是在模型层中放置许多表示类型的逻辑。例如，如果JSP页面需要以指定方式格式化的日期或者以指定方式排序的数据，某些人可能将该逻辑放置在模型层，对该逻辑来说，这是错误的地方。实际上，它应该在JSP页面使用的一组helper类中。当业务层返回数据时，Action&nbsp;Bean应该将数据转发给视图层。这样，无需创建模型和视图之间多余的耦合，就能够灵活支持多个视图层（JSP、Velocity、XML等）。也使视图能够确定向用户显示数据的最佳方式。 <BR>　　最后，我见过的大多数MVC应用程序都有未充分应用的控制器。例如，绝大多数的Struts应用程序将创建一个基本的Action类，并完成所有与安全相关的功能。其他所有的Action&nbsp;Bean都是此基类的派生类。这种功能应该是控制器的一部分，因为如果没有满足安全条件，则首先调用不应该到达Action&nbsp;Bean（即：模型）。记住，一个设计良好的MVC架构的最强大功能之一是存在一个健壮的、可扩展的控制器。您应该利用该能力以加强自己的优势。 <BR><BR>第5课：不要被JOPO束缚住手脚 <BR>　　我曾目睹许多项目为了使用Enterprise&nbsp;JavaBean而使用Enterprise&nbsp;JavaBean。因为EJB似乎给项目带来优越感和妄自尊大的表现，所以有时它是显酷的要素（coolness&nbsp;factor）。而其他时候，它会使J2EE和EJB引起混淆。记住，J2EE和EJB不是同意词。EJB只是J2EE&nbsp;的一部分，J2EE&nbsp;是包含JSP、servlet、Java&nbsp;消息服务（JMS）、Java数据库连接（JDBC）、JAAS、&nbsp;Java管理扩展（JMX）和EJB在内的一系列技术，同样也是有关如何共同使用这些技术建立解决方案的一组指导原则和模式。 <BR>　　如果在不需要使用EJB的情况下使用EJB，它们可能会影响程序的性能。与老的Web服务器相比，EJB一般对应用服务器有更多的需求。EJB提供的所有增值服务一般需要消耗更大的内存和更多的CPU时间。许多应用程序不需要这些服务，因此应用服务器要与应用程序争夺资源。 <BR>　　在某些情况下，不必要地使用EJB可能使应用程序崩溃。例如，最近我遇到了一个在开源应用服务器上开发的应用程序。业务逻辑封装在一系列有状态会话bean（EJB）中。开发人员为了在应用服务器中完全禁用这些bean的"钝化"费了很大的劲。客户端要求应用程序部署在某一商用应用服务器上，而该服务器是客户端技术栈的一部分。该应用服务器却不允许关闭"钝化"功能。事实上，客户端不想改变与其合作的应用服务器的设任何置。结果，开发商碰到了很大的麻烦。（似乎）有趣的事情是开发商自己都不能给出为什么将代码用EJB（而且还是有状态会话bean）实现的好理由。不仅仅是开发商会遇到性能问题，他们的程序在客户那里也无法工作。 <BR>　　在Web应用程序中，无格式普通Java&nbsp;对象（POJO）是EJB强有力的竞争者。POJO是轻量级的，不像EJB那样负担额外的负担。在我看来，对许多EJB的优点，例如对象入池，估计过高。POJO是您的朋友，不要被它束缚住手脚。 <BR><BR>第6课：数据访问并不能托管O/R映射&nbsp; <BR>　　我曾参与过的所有Web应用程序都向用户提供从其他地方存取的数据，并且因此需要一个数据访问层。这并不是说所有的项目都需要标识并建立这样一个层，这仅仅说明这样层的存在不是隐含的就是明确的。如果是隐含的数据层，数据层是业务对象（即：业务服务）层的一部分。这适用于小型应用程序，但通常与大一些项目所接受的架构指导原则相抵触。 <BR>　　总之，数据访问层必须满足或超出以下四个标准： <BR>　　具有透明性&nbsp; <BR>　　业务对象在不知道数据源实现的具体细节情况下，可以使用数据源。由于实现细节隐藏在数据访问层的内部，所以访问是透明的。 <BR>　　易于迁移 <BR>　　数据访问层使应用程序很容易迁移到其他数据库实现。业务对象不了解底层的数据实现，所以迁移仅仅涉及到修改数据访问层。进一步地说，如果您正在部署某种工厂策略，您可以为每个底层的存储实现提供具体的工厂实现。如果是那样的话，迁移到不同的存储实现意味着为应用程序提供一个新的工厂实现。 <BR>　　尽量减少业务对象中代码复杂性&nbsp; <BR>　　因为数据访问层管理着所有的数据访问复杂性，所以它可以简化业务对象和使用数据访问层的其他数据客户端的代码。数据访问层，而不是业务对象，含有许多与实现相关的代码（例如SQL语句）。这样给开发人员带来了更高的效率、更好的可维护性、提高了代码的可读性等一系列好处。 <BR>　　把所有的数据访问集中在单独的层上 <BR>　　由于所有的数据访问操作现在都委托给数据访问层，所以您可以将这个单独的数据访问层看做能够将应用程序的其他部分与数据访问实现相互隔离的层。这种集中化可以使应用程序易于维护和管理。 <BR>　　注意：这些标准都不能明确地调出对O/R（对象到关系）映射层的需求。O/R映射层一般用O/R映射工具创建，它提供对象对关系数据结构的查看和感知（look-and-feel）。在我看来，在项目中使用O/R映射与使用EJB类似。在大多数情况下，并不要求它。对于包含中等规模的联合以及多对多关系的关系型数据库来说，O/R映射会变得相当复杂。由于增加O/R&nbsp;映射解决方案本身的内在复杂性，例如延迟加载（lazy&nbsp;loading）、高速缓冲等，您将为您的项目带来更大的复杂性（和风险）。 <BR>　　为了进一步支持我的观点，我将指出按照Sun&nbsp;Microsystem所普及的实体Bean（O/R映射的一种实现）的许多失败的尝试，这是自1.0版以来一直折磨人的难题。在SUN的防卫措施中，一些早期的问题是有关EJB规范的开发商实现的。这依次证明了实体Bean规范自身的复杂性。结果，大多数J2EE架构师一般认为从实体Bean中脱离出来是一个好主意。 <BR>　　大多数应用程序在处理他们的数据时，只能进行有限次数的查询。在这样的应用程序中，访问数据的一种有效方法是实现一个数据访问层，该层实现执行这些查询的一系列服务（或对象、或API）。如上所述，在这种情况下，不需要O/R映射。当您要求查询灵活性时，O/R映射正合适，但要记住：这种附加的灵活性并不是没有代价的。 <BR>　　就像我承诺的那样，在本文中，我尽量避免陈腐的最佳实践。相反，关于J2EE项目中每一位架构师必须做出的最重要的决定，我集中讲解了我的观点。最后，您应该记住：J2EE并非某种具体的技术，也不是强行加入到解决方案中的一些首字母缩写。相反，您应该在适当的时机，恰当的地方，使用合适的技术，并遵循J2EE的指导原则和J2EE中所包含的比技术本身重要得多的实践。 <BR><img src ="http://www.blogjava.net/Victor/aggbug/22587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-05 15:25 <a href="http://www.blogjava.net/Victor/articles/22587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>