﻿<?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-挚爱.NET 情人J2EE-随笔分类-J2EE</title><link>http://www.blogjava.net/chenleiyu/category/7908.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:55:22 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:55:22 GMT</pubDate><ttl>60</ttl><item><title>用J2EE开发WebService</title><link>http://www.blogjava.net/chenleiyu/archive/2006/09/07/68338.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Thu, 07 Sep 2006 09:46:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/09/07/68338.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/68338.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/09/07/68338.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/68338.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/68338.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="3" width="760" align="center" border="0">
				<tbody>
						<tr width="100%">
								<td width="1%">
								</td>
								<!--开始正文-->
								<td width="98%">
										<br />
										<center>
												<br />
												<br />
												<br />
												<br />
												<img src="http://www.huihoo.com/xml/j2ee/tmc-logo.jpg" />
												<br />
												<b>I. 概要</b>
												<br />
												<br />基于XML的Web服务是参照B2B通信协作模式制定的新的规范。它提供了概念上和结构上的，适用于各种不同平台和产品的基础。现在，开发者可以利用J2EE技术来开发基于XML的Web服务。他们可以利用现存的J2EE技术来开发完整的，遵从XML标准的，能完全共通的WEB服务。无需重新设计或者构造现有的J2EE系统，开发人员就可以构建复杂的强大的Web服务应用。 <br /><br /><b>II. 介绍</b> Web服务是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求，轻量级的独立的通讯技术。这种技术允许网络上的所有系统进行交互。随着技术的发展，一个Web服务可以包含额外的指定功能并且可以在多个B2B应用中协作通讯。 <br /><br />Web服务正在不断完善，并且以一种非常智能的动态的方法来进行。这些灵活的Web服务可以理解请求中上下文的关系，并且在每一个特定的情况下产生动态的结果。这些服务会根据用户的身份，地点以及产生请求的原因来改变不同的处理，用以产生一个唯一的，定制的方案。这种协作机制对那些只对最终结果有兴趣的用户来说，是完全透明的。 <br /><br />这种Web服务所遵循的XML标准可以增进事物通信的性能。开发人员将可以利用不同的平台，产品和标准来实现很多种可能。通过这种标准，开发人员可以建立一个系统使他们的Web服务提供最大的协同工作的能力。 <br /><br />这份白皮书描述了如何方便地利用Java和XML技术来实现Web服务构架。它说明了Web服务中的每一个关键部分以及如何使他们结合在一起。你将会对基于XML的Web服务的结构以及如何与J2EE结合，有一个更加深入的了解我们从如何利用J2EE建立Web服务开始。这部分将使你对如何建立一个Web服务有一个了解。 <br /><br /><b>III. 总结</b> 一般来说，在不同的事务之间进行电子通信协作会有很多阻碍。全异的系统，安全限制和不相同的数据格式，导致很多B2B系统在他们自己的领域或者客户群中形成唯一。Web服务将改变这一切，使不同的事务互相通信变为可能，值得注意的是，这会降低建立商业站点的开发和维护成本。 <br /><br />在建立Web服务的时候，有三个主要步骤： <br /><br />1. 建立客户端联接 为了允许Applets，Applications，商业合作伙伴，浏览器和PDAs 使用Web服务。 <br /><br />2. 实现Web服务 包括工作流，数据传送，商业逻辑以及数据访问。这些功能是隐藏在Web服务后，并且为客户端工作的。 <br /><br />3. 联接后台系统 这个系统可能包括一个或多个数据库，现存的企业信息系统，商业合作伙伴自己的系统或者Web服务，以及在多个系统中共享的数据。 <br /><br />你可以利用J2EE来实现这三个目标。用J2EE开发Web服务基于以下两个技术： <br /><br />XML 技术. <br />在Web服务中，XML标准是非常重要的。XML是一种数据格式，它可以以一种连贯的方式来表现数据，并且可以在网络中以点对点的形式传送。这些不同的XML标准连同指定的处理方法是设计来支持特定的行为的。 <br /><br />Java 技术. <br />Developers开发人员利用 J2EE APIs来创建事务和表现的逻辑，访问XML文档，以及对XML文档进行操作。信任被证实可行的Java技术是非常重要的，因为它允许开发者利用现有的下部构造，在其上构建新的功能。开发者可以继续利用J2EE的标准API以及各种优秀的组件来开发系统。现在，开发者可以利用J2EE中提供的Java API for XML Parsing (JAXP) 来开发Web服务，我们将在稍后介绍。这个新的APIs主要用来处理XML数据格式以及服务，将使开发变得更容易，效率更高。 <br />图 1 表现了基于J2EE的Web服务的核心构架。请注意，很多APIs在这里并没有全部表示出来，象用来解析或者传送消息的。但是，那些基于J2EE的标准，协议以及主要的子系统都表示出来了。 <br /><img src="http://www.huihoo.com/xml/j2ee/figure1.jpg" /><br />图 1让我们进一步看一下利用J2EE来创建Web服务的细节。 <br /><br /><b>IV. 客户端联接</b><br /><br />客户端联接是关于Web服务的使用者如何来使用你的系统。表格 1 显示了三种主要使用系统的客户。 <br /><br /><table border="1"><tbody><tr><td>客户类型</td><td>样例</td><td>如何联接</td></tr><tr><td>商业合作伙伴</td><td>代理商，客户群</td><td>基于XML的Web 服务技术 (SOAP, UDDI, WSDL, ebXML)</td></tr><tr><td>瘦客户端</td><td>浏览器，PDAs，无线设备</td><td>HTTP 协议</td></tr><tr><td>胖客户端</td><td>应用小程序，应用程序，已经存在的系统</td><td>IIOP协议</td></tr></tbody></table><br />表格 1商业合作伙伴的联接 <br /><br />第一种访问Web服务的客户类型是商业合作伙伴。他们可能使用很多种类型的编程语言，中间件或者硬件。当他们访问尼的系统的时候，Web服务要求返回一个XML文件。这个文件具有标准的标记来表示商业数据，并且允许不同的系统通过这个来交互。 <br /><br /><b>Java Servlets</b><br /><br />当一个商业合作伙伴向Web服务发布一个请求的时候，接收请求的是一个Java servlet. 这个Servlet是一个在管理容器中运行，负责接收请求和响应的Java对象。它可以以很多种协议返回请求结果，象HTTP, FTP或者POP。在这个例子中Servlet通常使用HTTP来响应请求，这样的话，Web服务就可以利用HTTP来通过防火墙了。 <br /><br />当一个请求到达J2EE Web服务的时候，以下操作会发生，见图2 <br /><br />1. Java servlet接收XML 文档。 <br /><br />2. Servlet 处理传入的基于XML的请求 <br /><br />3. Servlet调用一个或者多个Enterprise JavaBeans (EJB) 组件来处理数据。 <br /><br />4. EJB组件进行他们自己的处理，可能会调用其他存在的系统。 <br /><br />5. EJB 组件把结果返回给Servlet。 <br /><br />6. Servlet 把结果汇集到XML文档中。 <br /><br />7. Servlet 把XML传送到客户端。 <img src="http://www.huihoo.com/xml/j2ee/figure2.jpg" /><br /><br />图 2为了实现商业合作伙伴的联接，必须有一种方法来发布，描述，定位以及调用一个Web服务。我们现在来描述如何达到这个目的。 <br /><br /><b>UDDI</b> 在用户能够调用Web服务之前，必须确定这个服务内包含哪些商务方法，找到被调用的接口定义，还要在服务端来编制软件。所以，我们需要一种方法来发布我们的Web服务。 <br /><br />UDDI (Universal Description, Discovery, and Integration) 是一个主要针对Web服务供应商和使用者的新项目。UDDI 项目中的成员可以通过UDDI Business Registry (UBR) 来操作Web服务的调用，UBR是一个全球性的服务。Web服务供应商可以在UBR中描述并且注册他们的服务。用户可以在UBR中查找并定位那些他们需要的服务。 <br /><br />UDDI是一种根据描述文档来引导系统查找相应服务的机制。UDDI包含标准的"白皮书"类型的商业查询方式，"黄皮书"类型的局部查找，以及"绿皮书"类型的服务类型查找。"绿皮书"允许开发者精确查找符合服务类型的所有服务。（这一段翻的比较奇怪） <br /><br />UDDI利用SOAP消息机制（标准的XML/HTTP）来发布，编辑，浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据，并且发送到注册中心或者由注册中心来返回需要的数据。 <br /><br /><b>JAXR</b><br /><br />为了支持UDDI在Java平台上的功能，Java APIs for XML Registries (JAXR)允许开发者来访问注册中心。值得注意的是，JAXR并不是建立Web服务必需的，你可以利用其他常用的XML APIs来直接集成这些协议。JAXR是一个方便的API，它提供了Java API来发布，查找以及编辑那些注册信息。它的重点在于基于XML的B2B应用，复杂的地址本查找以及对XML消息订阅的支持等Web服务。它也可以用来访问其他类型的注册中心，象ebXML注册中心（稍候描述）。 <br /><br />这些对Web服务的注册信息进行的操作，可以使用当前的一些Web服务工具来完成（例如第三方的SOAP和ebXML消息工具）。另外，当JAXP提供了一致并具有针对性的API来完成这些操作，这将使开发变得更加容易。 <br /><br /><b>WSDL</b><br /><br />对于商业用户来说，要找到一个自己需要使用的服务，他必须知道如何来调用。WSDL (Web Services Description Language) 规范是一个描述接口，语义以及Web服务为了响应请求需要经常处理的工作的XML文档。这将使简单地服务方便，快速地被描述和记录。 <br /><br />以下是一个WSDL的样例： <br /><br /></center>
										<p>&lt;?xml version="1.0"?&gt;<br />&lt;definitions name="StockQuote"<br />                targetNamespace="http://example.com/stockquote.wsdl"<br />                xmlns:tns="http://example.com/stockquote.wsdl"<br />                xmlns:xsd1="http://example.com/stockquote.xsd"<br />                xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"<br />                xmlns="http://schemas.xmlsoap.org/wsdl/"&gt;<br />&lt;types&gt;<br />   &lt;schema targetNamespace=http://example.com/stockquote.xsd<br />                 xmlns="http://www.w3.org/2000/10/XMLSchema"&gt;<br />      &lt;element name="TradePriceRequest"&gt;<br />        &lt;complexType&gt;<br />           &lt;all&gt;<br />                &lt;element name="tickerSymbol" type="string"/&gt;<br />              &lt;/all&gt;<br />           &lt;/complexType&gt;<br />        &lt;/element&gt;<br />        &lt;element name="TradePrice"&gt;<br />            &lt;complexType&gt;<br />                &lt;all&gt;<br />                   &lt;element name="price" type="float"/&gt;<br />                 &lt;/all&gt;<br />              &lt;/complexType&gt;<br />           &lt;/element&gt;<br />        &lt;/schema&gt;<br />     &lt;/types&gt;<br />     &lt;message name="GetLastTradePriceInput"&gt;<br />         &lt;part name="body" element="xsd1:TradePriceRequest"/&gt;<br />     &lt;/message&gt;<br />     &lt;message name="GetLastTradePriceOutput"&gt;<br />         &lt;part name="body" element="xsd1:TradePrice"/&gt;<br />    &lt;/message&gt;<br />    &lt;portType name="StockQuotePortType"&gt;<br />        &lt;operation name="GetLastTradePrice"&gt;<br />            &lt;input message="tns:GetLastTradePriceInput"/&gt;<br />            &lt;output message="tns:GetLastTradePriceOutput"/&gt;<br />         &lt;/operation&gt;<br />      &lt;/portType&gt;<br />      &lt;binding name="StockQuoteSoapBinding"<br />                    type="tns:StockQuotePortType"&gt;<br />         &lt;soap:binding style="document"<br />                                 transport="http://schemas.xmlsoap.org/soap/http"/&gt;<br />        &lt;operation name="GetLastTradePrice"&gt;<br />            &lt;soap:operation<br />                           soapAction="http://example.com/GetLastTradePrice"/&gt;<br />            &lt;input&gt;<br />               &lt;soap:body use="literal"/&gt;<br />            &lt;/input&gt;<br />            &lt;output&gt;<br />                &lt;soap:body use="literal"/&gt;<br />            &lt;/output&gt;<br />         &lt;/operation&gt;<br />      &lt;/binding&gt;<br />     &lt;service name="StockQuoteService"&gt;<br />        &lt;documentation&gt;My first service&lt;/documentation&gt;<br />        &lt;port name="StockQuotePort" binding="tns:StockQuoteBinding"&gt;<br />            &lt;soap:address location="http://example.com/stockquote"/&gt;<br />        &lt;/port&gt;<br />     &lt;/service&gt;<br />  &lt;/definitions&gt;<br /><br />它包含了以下的关键信息： <br />· 消息的描述和格式定义可以通过XML文档中的<types>和<message> 标记来传送。 <br />· <porttype>标记中表示了消息传送机制。 (e.g. request-only, request-response, response-only) 。 <br />· <binding>标记指定了编码的规范 。 <br />· <service>标记中表示服务所处的位置 (URL)。 <br /><br />WSDL在UDDI中总是作为一个接口描述文档。因为UDDI是一个通用的用来注册WSDL规范的地方，UDDI的规范并不限制任何类型或者格式描述文档。这些文档可能是一个WSDL文档，或者是一个正规的包含导向文档的Web页面，也可能只是一个包含联系信息的电子邮件地址。 <br /><br />现在Java提供了一个 Java API for WSDL (JWSDL)规范。它提供了一套能快速处理WSDL文档的方法，并且不用直接对XML文档进行操作，它会比JAXP更方便，更快速。 <br /><br />图 3 显示了如何使用WSDL 和 UDDI。 <br /><br /><img src="http://www.huihoo.com/xml/j2ee/figure3.jpg" /><br /><br />图 3 <br /><br /><b>SOAP</b><br /><br />当商业用户通过UDDI找到你的WSDL描述文档后，他通过可以Simple Object Access Protocol (SOAP) 调用你建立的Web服务中的一个或多个操作。 <br /><br />SOAP是XML文档形式的调用商业方法的规范，它可以支持不同的底层接口，象HTTP(S)或者SMTP。之所以使用XML是因为它的独立于编程语言，良好的可扩展性以及强大的工业支持。之所以使用HTTP是因为几乎所有的网络系统都可以用这种协议来通信，由于它是一种简单协议，所以可以与任何系统结合，还有一个原因就是它可以利用80端口来穿越过防火墙。 SOAP的强大是因为它简单。SOAP是一种轻量级的，非常容易理解的技术，并且很容易实现。它有工业支持，可以从各主要的电子商务平台供应商那里获得。 <br /><br />从技术角度来看，SOAP详细指明了如何响应不同的请求以及如何对参数编码。一个SOAP封装了可选的头信息和正文，并且通常使用HTTP POST方法来传送到一个HTTP 服务器，当然其他方法也是可以的，例如SMTP。SOAP同时支持消息传送和远程过程调用。以下是一个SOAP请求。 <br /><br /><p><font size="2">POST /StockQuote HTTP/1.1<br />Host: www.stockquoteserver.com<br />Content-Type: text/xml; charset="utf-8"<br />Content-Length: nnnn <br />SOAPAction: "Some-URI" <br /></font></p><p><font size="2">&lt;SOAP-ENV:Envelope<br />    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"<br />    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/&gt;<br />    &lt;SOAP-ENV:Header&gt;<br />        &lt;t:Transaction xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1"&gt;<br />           5<br />        &lt;/t:Transaction&gt;<br />    &lt;/SOAP-ENV:Header&gt;<br />    &lt;SOAP-ENV:Body&gt;<br />        &lt;m:GetLastTradePrice xmlns:m="Some-URI"&gt;<br />            &lt;symbol&gt;SUNW&lt;/symbol&gt;<br />        &lt;/m:GetLastTradePrice&gt;<br />    &lt;/SOAP-ENV:Body&gt;<br />&lt;/SOAP-ENV:Envelope&gt; </font></p><br /><b>JAX/RPC</b><br /><br />为了使开发人员专注于建立象SOAP那样的基于XML的请求，JCP正在开发基于RPC (JAX/RPC) 的Java API。JAX/RPC是用来发送和接收方法调用请求的，它基于XML协议，象SOAP，或者其他的象XMLP (XML Protocol，要了解更多可以参考http://www.w3.org/2000/xp/)。JAX/RPC使你不用再关注这些协议的规范，使应用的开发更快速。不久，开发人员就不用直接以XML表示方法调用了。 <br /><br />目前有很多第三方实现了SOAP，开发人员可以在不同的层次上调用SOAP，并选择使用哪一种。将来，JAX/RPC会取代这些APIs并提供一个统一的接口来构造以及处理SOAP RPC请求。 <br /><br />在接收一个从商业伙伴那里过来的SOAP请求的时候，一个Java servlet用JAX/RPC来接收这个基于XML的请求。一旦接收到请求后，servlet会调用商务方法，并且把结果回复给商业伙伴。 <br /><br /><b>ebXML</b> 对于具有高扩展性的商业交易来说，他们需要一种可信任的结构来实现商业事务，多请求的事务，计划以及文档流程，应用的需求经常超越了基于纯SOAP的实现。因为SOAP只是提供了一个底层的结构，而你可能需要一个更高级的框架结构。 <br /><br />ebXML就是为了这个目的的，它是一套处理B2B应用间的合作与通信的XML规范。以下是ebXML中的关键组件： <br /><br /><b>Collaboration Protocol Profile (CPP)</b><br /><br />CPP提供了一种标准并且简单的方法描述了公司提供的产品。另外，它还描述了消息交换的能力以及公司支持的商务合作。它也描述了公司的商务处理方法，包括合伙人如何与公司合作。CPP定义了B2B交易中双方的商务协作。例如，它同时定义了买卖双方的商务处理方法。 <br /><br /><b>Collaboration Protocol Agreement (CPA)</b><br /><br />CPA描述了两个公司之间进行交易时的详细需求以及机制。它包含有由手工或者自动从经过买卖双方认可的CPPs中的信息。这个CPA是双方进行指定交易的合约。 CPP和CPA的样例，以及关于规范的细节可以从以下网站获得： <br /><br />http://ebxml.org/project_teams/trade_partner/cpp-example.xml <br />http://ebxml.org/project_teams/trade_partner/cpa-example.xml <br />http://www.ebxml.org/specs/ebCCP.pdf <br /><br /><b>Business Process and Information Modeling</b><br /><br />ebXML还以XML的形式描述了商业事务处理的规范。它包括交易，文档流程，数字通信，数据封装格式以及其他更多。这些规范是用来建立CPPs，描述以及共享商业事务和信息时用的。 <br /><br /><b>Core Components</b><br /><br />ebXML中另外一个重要的部分是一系列的XML标记，我们叫它核心组件。这些标记包含了商务数据，象日期，税，账户，交易合同以及其他的。它指明了商业合约和实体，但对每个不同的行业，可能都不一样。 <br /><br /><b>Messaging</b><br /><br />ebXML消息格式包含了所有相关的消息导向信息（同步或者异步，可靠性）。一般来说，一个ebXML消息包含了CPA中的可视化内容，并强制执行交易规则。 EbXML是建立在SOAP消息封装机制上的。它扩展了SOAP的协议，增加了多层框架结构来支持附件，安全性以及传送的可靠性。 <br /><br /><b>Registry/Repository</b><br /><br />EbXML注册中心是存储CPPs, CPAs, ebXML核心组件和与ebXML相关的文档的服务。它具有强大的查询功能，允许用户查找相关的组件以及发掘潜在客户。JAXR API也可以用来访问ebXML注册中心。商业服务定义了CPPs，并且被存储在ebXML注册中心，然后发布到UDDI中。一个关键的概念是，UDDI提供了一个全球唯一的Web服务的描述信息，但那些真实的信息，还是保存在本地的ebXML库中。这样的话，一个潜在的客户首先到UDDI中查找相关内容，然后根据这些到ebXML库中找CPPs或者其他相关文档。 <br /><br /><b>JAXM</b><br /><br />当从商业合作伙伴那里接收一个Web服务的请求时，我们需要Java API实现一个Servlet来处理ebXML消息，就象我们用JAX/RPC来处理SOAP请求一样。 <br /><br /><b>Java API for XML Messaging (JAXM)</b><br /><br />是集成XML消息标准（象ebXML消息或者SOAP消息）的规范。这个API是用来推动XML消息处理的，它检测那些预定单的消息格式以及约束。它控制了所有的消息封装机制，用一种直观的方式分割了消息中的信息，象路由信息，发货单。这样，开发人员只要关注消息的有效负载，而不用去担心那些消息的重复处理。 <br /><br />目前的开发人员用JAXP来实现JAXM将要提供的功能，JAXM将会提供一套非常具有针对性的API来处理基于XML的消息传送。这将大大简化开发人员的代码，并使它们具有统一的接口。 <br /><br />JAXM和JAX/RPC的差别在于处理消息导向的中间件以及远程过程调用的不同。JAXM注重于消息导向，而JAX/RPC是用来完成远程过程调用的。以下是图解。 <br /><br /><img src="http://www.huihoo.com/xml/j2ee/figure4.jpg" /><br /><br />图 4 <br /><br />请注意，在JAXM 和 JAX/RPC技术成熟之前，开发人员还是依赖于第三方的SOAP APIs，象Apache SOAP, IdooXOAP, 以及 GLUE。当JAXM 和 JAX/RPC正式发布后，它将为当前不同的SOAP和ebXML消息提供统一的接口。就象JDBC位多种不同的数据库提供统一的接口。 <br /><br />以上是对于让商业合作伙伴访问你的Web服务的讨论。下面我们来讨论瘦客户端和胖客户端。 <br /><br /><b>Thin Client Connectivity</b><br /><br />瘦客户端（象浏览器或者无线设备）只对浏览页面感兴趣。Web服务的职责是执行需要处理的Web请求，象运行B2C交易，然后给出订单确认。为实现这个，开发者用JSP来写动态页面。JSP组件技术时一种可以根据后台数据处理的结果，来动态生成页面的技术。它们在提供JSP组件的容器中运行。 JSP可以表现后台用各种方法来实现的业务逻辑（e.g. EJBs，普通的Java对象，或者标准的JavaBean）。它可以生成标准的HTML或者XHTML来显示结果。 JSP组件与其说是可编程接口，不如说是用户界面。比方说，一个股票报价服务可能需要调用一个统计股票平均报价的应用中的Web服务，然后利用JSP技术把最终结果显示出来。以下显示了JSP组件的角色。 <br /><br /><img src="http://www.huihoo.com/xml/j2ee/figure5.jpg" /><br /><br />图 5 <br /><br /><b>Thick Client Connectivity</b><br /><br />有些Web服务的连接适合用胖客户端。比方说，公司的内部网。用户界面的响应以及功能可能更加重要。 <br /><br />一个胖客户端可以用很多种方法来联接Web服务。比方说，可以用UDDI, WSDL, SOAP以及ebXML。这是一个性能比较低的例子，因为客户端和服务端可能是由同样的开发组开发的，所以不需要处理很多的XML传送或者解析。 <br /><br />一个提高性能的方法是，胖客户端通过其他更有效的端口来联接，象Java RMI-IIOP。 <br /><br /><b>V. Implementing Web Services</b><br /><br />现在我们来看，如何在内部实现Web服务。 <br /><br /><b>数据传送和转换</b><br /><br />在进入Web服务之前，我们必须解决如何把传送进来的XML数据转换成我们自己的服务能够方便处理的格式，然后再把处理结果转换成XML格式返回给客户。因此一个开发人员需要一个强壮的机制来解析XML文件，绑定到Java对象，生成XML文件，并且传送各种不同的XML格式文件。有时由于我们的应用程序支持不同的接口（例如：B2B伙伴的SOAP，基于浏览器的HTML格式，或者是无线的WML访问同样的Web服务）我们可能需要不同的服务接口来处理这些不同客户端传送过来的请求。 <br /><br /><b>JAXP</b><br /><br />用来处理XML的Java APIs是一套Java本地接口，它提供了可插入到XSLT引擎中的接口SAX，DOM。这些构成了解析和处理XML文档的基础。这些APIs对Web服务来说，是非常底层的，它给了我们用Java来访问，修改以及创建XML文档的全部功能。 <br /><br />For more information, please see: <br /><br />http://java.sun.com/xml/tutorial_intro.html <br />http://java.sun.com/xml/xml_jaxp.html <br /><br /><b>JAXB</b><br /><br />XML绑定技术可以把XML文档和Java对象进行自由转换。用JAXB，你可以在后台的EJB层，把XML文档转换成Java对象。同样你也可以把从EJB中取出的Java对象转换成XML文档返回给用户。 <br /><br />JAXB接口提供了比SAX和DOM更高级的方法来处理XML文档。它提供的特性可以在XML数据和Java类之间互相映射，提供了一个简单的方法来转换XML数据。它比逐个解析标记更简单。 <br /><br /><b>XSLT</b><br /><br />从商业伙伴那里传送过来的XML文档可能和内部使用的格式不相同，比方说商业伙伴那里用"OrderNum"，而内部使用"OrderID"。 <br /><br />我们经常为了响应不同的客户请求，而重新格式化XML数据文档。举例来说，一个商业伙伴的请求可能传送一个SOAP表单，而一些浏览器用户可能是一个XHTML。在一个更复杂的系统中，我们可能需要支持很多种不同的表现形式，象WML表单或者VoiceXML。这要求我们有一种机制来把各种XML以基本的XML响应格式来传送给我们系统中不同的接口。 <br /><br /><b>XML Stylesheet Language Transformations (XSLT)</b><br /><br />是一种转换XML格式的机制。一个stylesheet可以指定一系列的模版对应规则，并把它们赋给一个可递归的，象DOM这样的模型。一个XSLT引擎可以用stylesheet来转换XML文档。XSLT stylesheet的语法是非常有表现力的，包含了循环，条件和数学表达式等。还有类似于函数（function）的机构和概念上的递归。 <br /><br /><b>Shared Context</b><br /><br />当两个商业发生交易的时候，通常有一个上下文的关系。这个关系是指定给合作伙办的一个协议，或者是一种商业规则，这样就可以给不同的合作伙伴进行交易。此外，一个商业协作在一段时间内可能调用不同的接口。每一个这样的调用都是处理同一个商业关系的，可能出现在整个商业生命周期中。 <br /><br />在J2EE Web服务中，为这个关系建一个离散的位置是一种建议的实现方法。作为一个开发人员，你应该在复杂的Web服务中需要这样的关系，并且为你的系统结构设计一个离散的组件来控制它。目前这种关系是通过数据库访问（JDBC）来实现的。但是，Context API可以把Web服务中需要对这种关系的访问操作作为一种流控制。这样，这些共享的数据就可以由各种组件来访问，象Servlet, JSP或者EJB组件。 <br /><br /><b>Business Layer</b><br /><br />当传送进来的XML数据被转换成Java对象后，这个数据已经准备被传送到EJB商务层做处理。EJB技术是一种用Java来创建商业组件的标准。用EJB组件，你可以从容器中得到一些服务，象安全性，状态保持，连接池，负载平衡以及失败恢复。 <br /><br />在EJB2.0标准中有3中EJB组件： <br /><br /><b>Session Beans</b><br /><br />进行客户端的工作。一般来说，Session Bean生命周期短，执行快速的操作，象提交订单，计算交易税额。 <br /><br /><b>Entity Beans</b><br /><br />表现商业数据。一般来说，Entity Bean生命周期长，并且映射到后台的存储介质内，象RDBMS或者OODBMS系统。Entity Bean分为两种类型：bean-managed persistent 和container-managed persistent <br /><br /><b>MessageDriven Beans</b><br /><br />是消息导向组件。它们通过消息导向中间件来接收消息象IBM MQSeries或者TIBCO Rendezvous。消息也可以通过Java客户端利用Java Message Service (JMS) 标准来发送。当消息到达后，一般用JMS API来访问。 <br /><br />一般来说，session beans 通过调用entity bean来完成希望的操作。比方说，一个用来计算订单价格的session bean，可能调用到表示产品和订单的entity bean。 Message-driven beans 用来接收消息，或者传送消息到那些session beans 或者 entity beans. <br /><br /><img src="http://www.huihoo.com/xml/j2ee/figure6.jpg" /><br /><br />图6显示了一个EJB组件交互的机制。 <br /><br />你可以用Java Naming和JNDI API来创建，查找以及删除EJB组件。这个API是用来访问J2EE发布系统中外部资源的标准API，可以访问包括数据库驱动，消息中间件，或者创建EJB的程序。 <br /><br />更多 EJB资料， 请查阅: <br /><br />· http://java.sun.com/products/ejb/white/white_paper.html <br />· http://java.sun.com/products/ejb/ <br />· http://www.theserverside.com <br />· "Mastering Enterprise JavaBeans" by Ed Roman, published by John Wiley &amp; Sons. <br /><br /><b>VI. Performing Back-End Integration</b><br /><br />最后来讨论用J2EE来开发Web服务的时候，如何与后台系统相连，象数据库，原先的系统和其他的商业伙伴。 <br /><br /><b>Database Connectivity</b><br /><br />为了联接关系数据库，开发人员必须选择APIs: The JDBC API 是一个用来访问支持SQL的关系数据库API.。（这个相信大家都知道了。） <br /><br /><b>SQL/J</b><br /><br />是用Java编写的标准的嵌入式SQL。类似于在HTML中嵌入JSP组件。 <br /><br /><b>Legacy System Connectivity</b><br /><br />在企业级开发中，与现存的系统相连接，一直都是一个比较困难的任务。大部分企业应用都是一个大杂烩，包含象SAP R/3, Siebel, i2以及一些客户服务系统。整合工作是一个手工任务，因为对现存的系统可用方案并不多。软件独立开发商被要求编写一个在任何平台上都可以运行的客户适配器，但这缺乏一个统一的标准平台。 <br /><br /><b>J2EE Connector Architecture (JCA)</b><br /><br />是在工业中应用的，一个针对现存系统的适配器。你可以用它来连接现在的系统，或者编写你自己的适配器。它可以运行在与任何J2EE兼容的环境中。用JCA，你只要编写一个适配器，就可以在任何J2EE环境中运行。对于软件独立开发商来说，这为他们提供了一个整合现存系统的方案。事实上，这些适配器正在开发中，对最终开发者来说，这的确是令人激动的。 <br /><br /><b>Business Partner Connectivity</b><br /><br />后台系统的最后一个类型是商业伙伴的Web服务。商业伙伴用全球认定的XML标准来暴露出一些他们自己的系统，在我们发布自己的Web服务时，可能会用到他们的这些服务。一般来说，UDDI用来注册Web服务，WSDL用来描述Web服务，SOAP和ebXML用来处理商务交易。 <br /><br />你的EJB组件可以调用JAP套件来访问商业伙伴的Web服务，这在之前已经介绍过了。 <br /><br />用 Java API for XML Registries (JAXR) 在UDDI注册中心查找商业伙伴的Web服务。 <br /><br />用 Java API for XML RPC (JAX/RPC) 处理到外部Web服务的请求。 <br /><br />用 Java API for XML Messaging (JAXM) 发送SOAP 或者 ebXML 消息到外部Web服务。 <br /><br />用 Java API for XML Parsing (JAXP) and the Java API for XML Binding (JAXB) <br /><br />把Java数据转换成适用于合作伙伴的XML格式。同样可以用来把合作伙伴那边的数据转换成易于自己处理的XML格式，或者进行XSLT数据转换。 <br /><br />结合使用Java标准APIs和J2EE Web服务构架，我们就可以建立强大的跨平台的系统。利用它们，我们可以与商业伙伴共享数据，提供完整的end-to-end的Web服务解决方案。见图7。 <br /><img src="http://www.huihoo.com/xml/j2ee/figure7.jpg" /><br /><br /></service></binding></porttype></message></types></p>
								</td>
								<!--结束正文-->
								<td width="1%">
								</td>
						</tr>
				</tbody>
		</table>
		<!--结束中部-->
		<!--开始底部-->
		<script language="javascript" src="../../public/bottom.js">
		</script>
		<table cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td align="middle">
										<font size="2">
										</font>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/68338.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-09-07 17:46 <a href="http://www.blogjava.net/chenleiyu/archive/2006/09/07/68338.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)hibernate查询语句--HQL </title><link>http://www.blogjava.net/chenleiyu/archive/2006/08/20/64576.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Sat, 19 Aug 2006 17:25:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/08/20/64576.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/64576.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/08/20/64576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/64576.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/64576.html</trackback:ping><description><![CDATA[1 .from<br /><br />1.1单表查询<br /><br />from eg.cat as cat.其中，cat只是一个别名，为了用其他子语句的时候书写简单<br /><br />1.2多表查询<br /><br />from eg.Cat,eg.Dog<br />from eg.Cat as cat,eg.Dog as dog<br />2 join相关<br />(inner) join<br />left (outer) join<br />right (outer) join<br />full join<br />HQL同样对SQL中的这些特性支持<br />下面插播一个小话题，关于上边的那些特性，我一直都没怎么用，今天既然说到这里，就想<br />把上边的几个特性的用法说一下，也算对自己的一个补充：<br />假设有两个表：部门、员工，下面列举一些数据：<br />员工(Employee)：<br />ID Name DepNo<br />001 Jplateau 01<br />002 Jony 01<br />003 Camel 02<br />部门(Department)：<br />ID Name<br />01 研发部<br />02 营销部<br /><br />在Hibernate中我们操纵的都是对象，所以我们操纵的是部门类和员工类<br />1).(inner) join<br />select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name<br />as name2 from Employee as employee join Department as department on employee.DepNo=<br />department.ID (注意到条件语句我用on 没有用where)<br />那么执行结果是什么呢？<br />id1 name1 id2 name2<br />++++++++++++++++++++++++++++++++++++++<br />001 Jplateau 01 研发部<br />002 Jony 01 研发部<br /><br />2).left (outer) join<br />select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name<br />as name2 from Employee as employee left join Department as department on employee.DepNo=<br />department.ID <br />那么执行结果又该是什么呢？<br />id1 name1 id2 name2<br />++++++++++++++++++++++++++++++++++++++<br />001 Jplateau 01 研发部<br />002 Jony 01 研发部 <br />003 Camel null null <br />{就是说此时我要已第一个表的记录多少为准，第二个表中没有相应纪录的时候填充null} <br />3). right (outer) join<br />select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name<br />as name2 from Employee as employee right join Department as department on employee.DepNo=<br />department.ID <br />那么执行结果又该是什么呢？<br />id1 name1 id2 name2<br />++++++++++++++++++++++++++++++++++++++<br />001 Jplateau 01 研发部<br />002 Jony 01 研发部 <br />null null 02 营销部 <br />{就是说此时我要已第二个表的记录多少为准，第一个表中没有相应纪录的时候填充null} <br /><br />3 大小写敏感<br /><br />4。select语句<br />就是要确定你要从查询中返回哪些对象或者哪些对象的属性。写几个例子吧：<br />select employee form Employee as employee <br />select employee form Employee as employee where employee.Name like 'J%'<br />select employee.Name form Employee as employee where employee.Name like 'J%'<br />select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name<br />as name2 from Employee as employee right join Department as department on employee.DepNo=<br />department.ID <br /><br />select elements(employee.Name) from Employee as employee <br />（不明白elements到底是做什么用的？望给于说明） <br />等等<br />5。数学函数<br />JDO目前好像还不支持此类特性。<br />avg(...), sum(...), min(...), max(...) <br /><br />count(*) <br /><br />count(...), count(distinct ...), count(all...) <br /><br />其用法和SQL基本相同<br /><br />select distinct employee.name from Employee as employee <br />select count(distinct employee.name),count(employee) from Employee as employee <br /><br />6。polymorphism (暂时不知道如何解释？)<br />from com.test.Animal as animal<br />不光得到所有Animal得实例，而且可以得到所有Animal的子类（如果我们定义了一个子类Cat）<br />一个比较极端的例子<br />from java.lang.Object as o<br />可以得到所有持久类的实例<br /><br />7。where语句<br />定义查询语句的条件，举几个例子吧：<br />from Employee as employee where employee.Name='Jplateau'<br />from Employee as employee where employee.Name like 'J%'<br />from Employee as employee where employee.Name like '%u'<br />在where语句中“=”不光可以比较对象的属性，也可以比较对象，如：<br />select animal from com.test.Animal as animal where animal.name=dog<br /><br />8。表达式<br /><br />在SQL语句中大部分的表达式在HQL中都可以使用：<br />mathematical operators +, -, *, / <br /><br />binary comparison operators =, &gt;=, &lt;=, &lt;&gt;, !=, like <br /><br />logical operations and, or, not <br /><br />string concatenation || <br /><br />SQL scalar functions like upper() and lower() <br /><br />Parentheses ( ) indicate grouping <br /><br />in, between, is null <br /><br />JDBC IN parameters ? <br /><br />named parameters :name, :start_date, :x1 （这种应该是另一种"?"的变通解决方法）<br /><br />SQL literals 'foo', 69, '1970-01-01 10:00:01.0' <br /><br />Java public static final constants eg.Color.TABBY <br /><br />其他不必解释了，在这里我只想对查询中的参数问题说明一下：<br />大家知道在SQL中进行传递参数进行查询的时候，我们通常用PreparedStatement，在语句中写一大堆的“？”，<br />在hql中也可以用这种方法，如：<br />List mates = sess.find(<br />"select employee.name from Employee as employee " +<br />"where employee.Name=? ",<br />name,<br />Hibernate.STRING<br />);<br />(说明：上面利用Session里的find方法，在hibernate的api Session中重载了很多find方法，它可以满足你多种形式的查询)<br />上边是一个参数的情形，这种情况下紧接着引入参数和定义参数的类型，当为多个参数，调用另一个find方法，它的后两个<br />参数都是数组的形式。<br /><br />还有另外一种方法来解决上边的问题，JDO也有这样的方法，不过和hibernate的表现形式上有差别，但他们两个骨子里却是<br />一样的，如：<br />Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");<br />q.setString("name", "Jplateau");<br />//当有多个参数的时候在此逐一定义<br />Iterator employees = q.iterate(); <br /><br />9。order 语句<br />和sql语句没什么差别，如：<br />select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (或者asc)<br /><br />10。group by 语句<br />同样和sql语句没什么差别，如：<br /><br />select employee.name,employee.DepNo from Employee as employee group by employee.DepNo<br /><br />select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id<br />{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}<br />谁帮我解释一下上边两句，谢过！<br /><br />11。子查询<br />hibernate同样支持子查询，写几个例子：<br /><br />from eg.Cat as fatcat where fatcat.weight &gt; ( select avg(cat.weight) from eg.DomesticCat cat )<br /><img src ="http://www.blogjava.net/chenleiyu/aggbug/64576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-08-20 01:25 <a href="http://www.blogjava.net/chenleiyu/archive/2006/08/20/64576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)MyEclipse EJB Project 快速开始</title><link>http://www.blogjava.net/chenleiyu/archive/2006/07/06/56936.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Thu, 06 Jul 2006 05:39:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/07/06/56936.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/56936.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/07/06/56936.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/56936.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/56936.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. 前言								为了学习EJB 3.0 ，我学习EJB2.0		因此我遇见了这篇文章，顺便就翻译了出来。我确实服了老外，半天说不到关键点上，说了好多的废话。不过话又说回来，他们对知识的严谨，对问题的认真，让我跟也加佩服了，也因此坚持的翻译下来了，两个晚上不白费哈。		希望对同路人能有所帮助哈。		 																							...&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/07/06/56936.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/56936.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-07-06 13:39 <a href="http://www.blogjava.net/chenleiyu/archive/2006/07/06/56936.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)Google Web Toolkit 入门</title><link>http://www.blogjava.net/chenleiyu/archive/2006/07/05/56751.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Wed, 05 Jul 2006 06:46:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/07/05/56751.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/56751.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/07/05/56751.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/56751.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/56751.html</trackback:ping><description><![CDATA[
		<p>
				<a name="N10053">
						<span class="atitle">
								<strong>
										<font size="4">Ajax简介</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Ajax是 Asynchronous JavaScript and XML（以及 DHTML 等）的缩写，由XHTML、CSS、JavaScript、XMLHttpRequest、XML等技术组合而成，是当前Web应用开发领域的热门技术，用于创建更加动态和交互性更好的Web应用程序，提升用户的浏览体验。</p>
		<p>Ajax的核心是JavaScript对象XmlHttpRequest。XmlHttpRequest处理所有服务器通信的对象，是一种支持异步请求的技术。简而言之，XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应，而不阻塞用户。</p>
		<p>Ajax并不是本文的中心关注点，因此这里不再赘述，请大家访问参考资源区的相关链接，了解更多关于Ajax技术和应用方面的知识。</p>
		<p>
				<a name="N10062">
						<span class="atitle">
								<strong>
										<font size="4">为什么选择GWT？</font>
								</strong>
						</span>
				</a>
		</p>
		<p>众所周知，即使对于Ajax技术非常熟悉的开发者而言，Ajax应用的开发和调试过程也不是一件容易的事情，更困难的是，到目前为止，一直没有出现合适的开发工具能够支持Ajax应用的开发和调试。</p>
		<p>与此相反的是，Java语言--企业应用开发的主流语言-的开发和调试过程却因为有各种各样开发工具的支持而简单的多，而且这样的开发工具我们可以免费获得，比如Eclipse、NetBeans。</p>
		<p>如果能够应用Java语言开发Ajax，Ajax应用开发的最大难题-开发工具的缺失-就将迎刃而解。这种情况下，我们就可以既充分利用Java语言的开发优势降低Ajax应用开发的难度，加快Ajax应用的开发速度，为Ajax的大规模应用创造可能，又可以充分发挥Ajax技术的优势，创建更加动态和交互性更好的Web应用程序，提升用户的浏览体验。</p>
		<p>Google Web Toolkit(简称GWT)的出现为我们提供了这种可能。GWT是Google推出的Ajax应用开发包，支持开发者使用Java语言开发Ajax应用。GWT框架本身是开源的，但是GWT中的开发工具仅仅提供开发用License，不允许分发。</p>
		<p>GWT提供了一组基于Java语言的开发包，这个开发包的设计参考Java AWT包设计，类命名规则、接口设计、事件监听等都和AWT非常类似。熟悉Java AWT的开发者不需要花费多大的力气就能够快速的理解GWT开发工具包，将更多地时间投入到GWT应用的开发过程中。</p>
		<p>开发出来的Java应用将由GWT开发包提供的编译工具编译后声生成对应的、应用了Ajax技术的Web应用，Java应用中出现的、和服务器之间的交互动作被自动生成的异步调用代码所代替。</p>
		<p>
				<a name="N1007A">
						<span class="atitle">
								<strong>
										<font size="4">GWT特性</font>
								</strong>
						</span>
				</a>
		</p>
		<p>GWT除了支持将应用Java语言开发的应用转化为Ajax应用，同时提供了更多的高级特性，下面是这些特性的简单描述。</p>
		<p>1. GWT编译器</p>
		<p>GWT编译器是GWT的核心，负责完成将Java代码翻译很Ajax内容的工作。GWT编译器能够翻译Java语言的大部分特性。包括支持Java语言中的基本类型、违例处理等，支持java.lang包和java.util包中的绝大部分类和接口，支持正则表达式和序列化。</p>
		<p>2. 跨平台支持</p>
		<p>如果你使用GWT中提供的显示组件(比如Button)和组装组件(比如VerticalPanel)，GWT编译生成的Ajax应用能够支持大部分的浏览器和操作系统，比如Internet Explorer、Firefox等，也能够支持Linux、Windows等不同操作系统。这是因为GWT最大限度的将这些控件翻译成浏览器内置的类型。比如Button类编译后生成的是标准HTML：&lt;input type="button"&gt;。</p>
		<p>GWT建议使用CSS修饰页面元素的显示效果。GWT的类中很少提供访问页面元素样式属性的方法，我们可以直接在CSS文件中通过对应的样式名称来设置页面元素的默认显示效果。比如使用 .gwt-Button { font-size: 150%; } 使用Button元素的默认显示效果。</p>
		<p>3. 宿主模式(Hosted Mode)</p>
		<p>宿主模式是指我们和没有转换为Ajax应用的GWT应用交互的状态。当我们开发和调试时，我们就一直处在宿主模式下。在这种情况下，Java虚拟机使用GWT内置的浏览器运行GWT应用编译后的class内容，因此能够提供"编码、测试、调试"过程的最佳速度。</p>
		<p>我们可以运行com.google.gwt.dev.GWTShell启动宿主模式。</p>
		<p>4. Web模式(Web Mode)</p>
		<p>Web模式是指已经成功转化为Ajax应用的状态，这种状态下，我们已经开始通过Web方式来访问Ajax应用了。</p>
		<p>在Web模式下运行时，不再需要GWT工具包或者JVM的支持。</p>
		<p>5. 命令行工具</p>
		<p>GWT工具包中提供了几个非常适用的小工具来帮助我们更快的建立GWT应用开发环境：projectCreator、applicationCreator、junitCreator。</p>
		<ul>
				<li>projectCreator <br />创建在Eclipse中开发GWT应用所需要的项目基本文件和可选的Ant buildfile文件。 
</li>
				<li>applicationCreator <br />applicationCreator命令用于创建基本的HelloWorld！应用和GWT应用开发环境。 
</li>
				<li>junitCreator <br />生成junti测试代码。 </li>
		</ul>
		<p>通过上面的内容，我们已经了解了GWT工具集工作的基本原理和主要特性。有些迫不及待了吧，那么就请随我一起进入GWT应用开发的过程吧，享受应用Java语言开发Ajax应用带来的简单和便利。</p>
		<p>本文中所有的环境准备、实例开发和说明均针对Windows操作平台，如果使用其他的操作系统，请根据实际情况进行适当的调整。</p>
		<p>
				<a name="N100C2">
						<span class="atitle">
								<strong>
										<font size="4">环境准备</font>
								</strong>
						</span>
				</a>
		</p>
		<p>1、 下载和安装JDK1.4.X </p>
		<p>GWT工具包的编译需要JDK支持，因此在安装GWT工具包之前请下载和安装合适的JDK。GWT工具支持Java语言1.4版本或者以下版本，因此JDK版本选择JDK1.4.X是比较合适的，不需要采用最新的JDK5.0或者更高版本。</p>
		<p>请访问java.sun.com网站上下载安装版本，下载后安装到C:/jdk目录下，本书中的后续内容中将使用%JAVA_HOME%变量来引用这个目录。</p>
		<p>您可以根据实际情况将JDK安装到任意目录下。但是在运行本文中例子的时候，请记住将对应的%JAVA_HOME%变量替换为您的实际安装目录。</p>
		<p>2、 下载和安装GWT</p>
		<p>请访问<a href="http://code.google.com/webtoolkit/" target="_blank"><font color="#5c81a7">http://code.google.com/webtoolkit/</font></a>下载GWT的最新版本，将下载的压缩文件解压缩到C:/GWT目录下。本书中的后续内容中将使用%GWT_HOME%变量来引用这个目录。</p>
		<p>GWT工具包支持不同的操作系统，请根据自己的操作系统选择合适的安装包。</p>
		<p>您可以根据实际情况将GWT安装到任意目录下。但是在运行本文中例子的时候，请记住将对应的%GWT_HOME%变量替换为您的实际安装目录。</p>
		<p>
				<a name="N100E4">
						<span class="atitle">
								<strong>
										<font size="4">第一个例子-Hello World！</font>
								</strong>
						</span>
				</a>
		</p>
		<p>下面的内容中我们将介绍如何使用GWT工具集来完成第一个GWT的例子-"Hello World！"，并且将使用GWT编译及将他转化为Ajax应用，在浏览器中完成测试。</p>
		<p>我们要完成的例子要实现的功能包括：</p>
		<p>1、 在页面上显示一个按钮</p>
		<p>2、 点击该按钮，默认情况下，我们将在按钮后面紧跟着显示字符串"Hello World!"。</p>
		<p>3、 如果点击按钮时，"Hello World!"字符串已经显示在浏览器中，我们要将他隐藏起来。我们现在开始使用GWT工具集完成"Hello World！"例子的开发，下面的步骤是完成"Hello World！"例子开发环境配置、应用开发、编译的基本步骤，同样适用于其他GWT应用的开发，只是根据实际情况可能有增减。</p>
		<p>1、 创建GWT应用开发环境</p>
		<p>从上面的GWT特性部分我们知道，GWT工具包中提供的applicationCreator命令行工具可以帮助我们创建GWT应用开发所需要的环境，因此我们可以直接使用applicationCreator帮助我们完成这项工作。</p>
		<p>打开命令行工具，进入C:/根目录下，执行"mkdi"命令创建新的文件目录HelloWorld。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">C:\&gt; mkdir HelloWorld
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>执行下面的命令将%JAVA_HOME%\bin目录和%GWT_HOME%目录加入到PATH路径中。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">C:\&gt;set path=%JAVA_HOME%\bin;%GWT_HOME%
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>请将命令行中的%JAVA_HOME%替换为实际环境中JDK的安装目录，将%GWT_HOME%替换为GWT工具包的安装目录。</p>
		<p>进入新创建的HelloWorld目录，然后运行applicationCreator命令创建GWT应用开发环境。</p>
		<p>applicationCreator.cmd命令支持的语法如下。</p>
		<p>ApplicationCreator [-eclipse projectName] [-out dir] [-overwrite] [-ignore] className 其中最重要的一个参数是classname，也就是我们创建的GWT应用中的主Java类，我们这里选择使用org.vivianj.gwt.client.HelloWorld。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">C:\HelloWorld&gt;applicationCreator.cmd org.vivianj.gwt.client.HelloWorld
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>根据GWT工具包的默认规则，GWT应用中的主Java类报名的最后一段必须是client。也就是说，只有xxx.client.Xxxx这样命名的Java类才能被识别为正确的GWT应用主类。</p>
		<p>ApplicationCreator工具运行的时候，屏幕上会打印如下内容。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">Created directory C:\HelloWorld\src
Created directory C:\HelloWorld\src\org\vivianj\gwt
Created directory C:\HelloWorld\src\org\vivianj\gwt\client
Created directory C:\HelloWorld\src\org\vivianj\gwt\public
Created file C:\HelloWorld\src\org\vivianj\gwt\HelloWorld.gwt.xml
Created file C:\HelloWorld\src\org\vivianj\gwt\public\HelloWorld.html
Created file C:\HelloWorld\src\org\vivianj\gwt\client\HelloWorld.java
Created file C:\HelloWorld\HelloWorld-shell.cmd
Created file C:\HelloWorld\HelloWorld-compile.cmd
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>2、 开发"Hello World！"例子</p>
		<p>使用ApplicationCreator工具的时候，ApplicationCreator工具其实已经帮我们生成了符合Hello World!例子要求的全部内容。为了不打断第一个例子的演示过程，我们先简单的了解一下ApplicationCreator工具的生成内容。我们将在将在测试过程后面做出更加详细的分析。</p>
		<p>图1中显示了ApplicationCreator工具执行后生成的目录结构。</p>
		<p>
				<br />
				<a name="N10146">
						<strong>图1 GWT应用开发环境</strong>
				</a>
				<br />
				<img height="176" alt="图1  GWT应用开发环境" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image003.jpg" width="214" border="0" />
				<br />
		</p>
		<p>图1中的src\org\vivianj\gwt\client目录中的HelloWorld.java是GWT应用的主类；src\org\vivianj\gwt\public目录中的HelloWorld.html文件是例子的默认页面；src\org\vivianj\gwt目录下的HelloWorld.gwt.xml是GWT应用的配置文件，提供GWT应用中页面和主类的配置信息；根目录下的HelloWorld-compile.cmd文件用于提供将该GWT应用编译成Ajax的命令；根目录下的HelloWorld-shell.cmd文件用于启动宿主模式(Hosted Mode)，方便测试GWT应用。</p>
		<p>3、 在宿主模式下启动"Hello World！"例子</p>
		<p>我们可以直接在命令行中执行HelloWorld-shell.cmd来启动宿主模式(Hosted Mode)，运行新创建的"Hello World！"例子。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">C:\HelloWorld&gt;HelloWorld-shell.cmd
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这个命令将启动两个可视化界面：Google Web Toolkit Development Shell（见图2）和GWT内置的GWT应用浏览器（见图3），GWT应用浏览器中将显示"Hello World！"例子的初始界面，如果我们点击界面上的"Click Me"按钮，按钮后面将会显示"Hello World!"字符串（见图4），如果再次单击页面上的"Click Me"按钮，按钮后面的"Hello World！"字符串会消失。</p>
		<p>
				<br />
				<a name="N1016D">
						<strong>图2 Google Web Toolkit Development Shell运行界面</strong>
				</a>
				<br />
				<img height="412" alt="图2  Google Web Toolkit Development Shell运行界面" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image005.jpg" width="480" border="0" />
				<br />
				<br />
				<a name="N1017F">
						<strong>图3 "Hello World！"例子初始运行界面</strong>
				</a>
				<br />
				<img height="359" alt="图3  Hello World！例子初始运行界面" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image007.jpg" width="480" border="0" />
				<br />
				<br />
				<a name="N10191">
						<strong>图4 "Hello World！"例子-单击"Clieck Me"按钮后的界面</strong>
				</a>
				<br />
				<img height="341" alt="图4  Hello World！例子-单击Clieck Me按钮后的界面" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image009.jpg" width="456" border="0" />
				<br />
		</p>
		<p>4、 编译"Hello World！"例子</p>
		<p>要将GWT应用编译成Ajax应用，我们可以执行HelloWorld-compile.cmd。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">C:\HelloWorld&gt;HelloWorld-compile.cmd
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>命令运行时，界面上将会显示下面的内容。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">Output will be written into C:\HelloWorld\www\org.vivianj.gwt.HelloWorld
Copying all files found on public path
Compilation succeeded
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>其中的第一行显示生成的Ajax应用位于C:\HelloWorld\www\org.vivianj.gwt.HelloWorld目录下。</p>
		<p>
				<br />
				<a name="N101C1">
						<strong>图5 "Hello World！"例子编译后的目录结构</strong>
				</a>
				<br />
				<img height="322" alt="图5  Hello World！例子编译后的目录结构" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image011.jpg" width="340" border="0" />
				<br />
		</p>
		<p>从上面的图中我们可以看到，新生成的www目录下有一个名为org.vivianj.gwt.HelloWorld的目录，它的命名规则是GWT主类全名(org.vivianj.gwt.client.HelloWorld)去掉其中的"client."。</p>
		<p>org.vivianj.gwt.HelloWorld目录下的HelloWorld.html文件就是"Hello World！"例子对应的页面，以.cache.html后缀结尾的文件就是"Hello World！"例子中对应的Ajax代码部分，而gwt.js文件则是GWT提供的、Ajax代码中需要用到的JavaScript公共函数。其他还有些辅助文件。</p>
		<p>5、 Web模式下测试"Hello World！"例子</p>
		<p>运行HelloWorld-compile.cmd后，GWT应用就已经被编译成Ajax应用了，不再依赖于JDK和GWT环境，而仅仅依赖于浏览器。</p>
		<p>我们打开IE浏览器，打开C:\HelloWorld\www\org.vivianj.gwt.HelloWorld\HelloWorld.html文件，就可以看到"Hello World！"例子在Web模式下的运行效果（见图6），单击页面上的"Click Me"按钮，按钮后面会出现"Hello World！"字符串（见图7），如果再次单击页面上的"Click Me"按钮，按钮后面的"Hello World！"字符串会消失。</p>
		<p>
				<br />
				<a name="N101E2">
						<strong>图6 Web模式下运行"HelloWorld！"例子的默认效果</strong>
				</a>
				<br />
				<img height="308" alt="图6  Web模式下运行 HelloWorld！例子的默认效果" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image013.jpg" width="553" border="0" />
				<br />
				<br />
				<a name="N101F4">
						<strong>图7 Web模式下运行"HelloWorld！"例子-单击"Click Me"按钮后的效果</strong>
				</a>
				<br />
				<img height="308" alt="图7  Web模式下运行 HelloWorld！例子-单击 Click Me按钮后的效果" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-gwt-intro/images/image015.jpg" width="553" border="0" />
		</p>
		<p>
				<a name="N10204">
						<span class="atitle">
								<strong>
										<font size="4">实例详细分析</font>
								</strong>
						</span>
				</a>
		</p>
		<p>上面的步骤中，我们已经完成第一个GWT应用的编译和测试，下面我们来详细的介绍一下GWT应用开发过程中涉及的几个文件：显示页面HelloWorld.html、主类HelloWorld和配置文件HelloWorld.gwt.xml。</p>
		<p>1. 显示页面HelloWorld.html</p>
		<p>GWT应用中的HTML内容必须存放在public目录下，public目录的全路径是"org\vivianj\gwt\ublic"，是将主类HelloWorld的包名"org.vivianj.gwt.client"中的client替换为"public"后形成的目录结构。</p>
		<p>我们将主要介绍HelloWorld.html中和GWT相关的内容，HelloWorld.html文件的全部内容见清单1。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">24.    &lt;meta name='gwt:module' content='org.vivianj.gwt.HelloWorld'&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在HelloWorld.html文件的第24行，我们使用meta标签指定了本页面和GWT模块" org.vivianj.gwt.HelloWorld"之间的联系，meta标签的name属性是不变的，而content属性则是GWT主类全名(org.vivianj.gwt.client.HelloWorld)去掉其中的"client."后的结果。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">40.	    &lt;script language="javascript" src="gwt.js"&gt;&lt;/script&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在HelloWorld.html文件的第40行，我们使用script标记引入GWT工具提供的默认函数库。根据HTML语言的规定，我们可以选择在&lt;head&gt;&lt;/head&gt;之间加入这行代码，也可以选择在&lt;body&gt;&lt;/body&gt;之间加入这段代码。GWT建议在&lt;head&gt;&lt;/head&gt;之间加入这行代码，以便获得稍微快一些的起动速度。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">56.	&lt;td id="slot1"&gt;&lt;/td&gt;&lt;td id="slot2"&gt;&lt;/td&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在HelloWorld.html文件的第56行，我们使用td标签定义了两个相邻的表格单元格，他们分别命名为"slot1"和"slot2"，根据"HelloWorld!"例子的要求，名为"slot1"的单元格中应该显示按钮，而名为"slot2"的单元格中则根据情况决定显示"HelloWorld!"字符串或者不显示任何内容。</p>
		<p>我们将在主类HelloWorld.java的源代码中使用RootPanel.get("slot1")来获得对该单元格的引用，请参考下面的"主类HelloWorld"部分的说明。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">43.	&lt;iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"&gt;&lt;/iframe&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在HelloWorld.html的第43行，我们使用iframe标签来设置当前页支持历史功能，iframe的属性必须和上面的内容保持一致，否则将不会起到任何效果。</p>
		<br />
		<a name="N10249">
				<strong>清单1 src\org\vivianj\gwt\public\public\HelloWorld.html</strong>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">1.	&lt;html&gt;
2.		&lt;head&gt;
3.		
4.			&lt;!--                                           --&gt;
5.			&lt;!-- Any title is fine                         --&gt;
6.			&lt;!--                                           --&gt;
7.			&lt;title&gt;Wrapper HTML for HelloWorld&lt;/title&gt;
8.	
9.			&lt;!--                                           --&gt;
10.			&lt;!-- Use normal html, such as style            --&gt;
11.			&lt;!--                                           --&gt;
12.			&lt;style&gt;
13.				body,td,a,div,.p{font-family:arial,sans-serif}
14.				div,td{color:#000000}
15.				a:link,.w,.w a:link{color:#0000cc}
16.				a:visited{color:#551a8b}
17.				a:active{color:#ff0000}
18.			&lt;/style&gt;
19.	
20.			&lt;!--                                           --&gt;
21.			&lt;!-- The module reference below is the link    --&gt;
22.			&lt;!-- between html and your Web Toolkit module  --&gt;		
23.			&lt;!--                                           --&gt;
24.			&lt;meta name='gwt:module' content='org.vivianj.gwt.HelloWorld'&gt;
25.			
26.		&lt;/head&gt;
27.	
28.		&lt;!--                                           --&gt;
29.		&lt;!-- The body can have arbitrary html, or      --&gt;
30.		&lt;!-- you can leave the body empty if you want  --&gt;
31.		&lt;!-- to create a completely dynamic ui         --&gt;
32.		&lt;!--                                           --&gt;
33.		&lt;body&gt;
34.	
35.			&lt;!--                                            --&gt;
36.			&lt;!-- This script is required bootstrap stuff.   --&gt;
37.			&lt;!-- You can put it in the HEAD, but startup    --&gt;
38.			&lt;!-- is slightly faster if you include it here. --&gt;
39.			&lt;!--                                            --&gt;
40.			&lt;script language="javascript" src="gwt.js"&gt;&lt;/script&gt;
41.	
42.			&lt;!-- OPTIONAL: include this if you want history support --&gt;
43.			&lt;iframe id="__gwt_historyFrame"
style="width:0;height:0;border:0"&gt;&lt;/iframe&gt;
44.	
45.			&lt;h1&gt;HelloWorld&lt;/h1&gt;
46.	
47.			&lt;p&gt;
48.			This is an example of a host page for the HelloWorld application. 
49.			You can attach a Web Toolkit module to any HTML page you like, 
50.			making it easy to add bits of AJAX functionality to existing
pages 
51.			without starting from scratch.
52.			&lt;/p&gt;
53.	
54.			&lt;table align=center&gt;
55.				&lt;tr&gt;
56.					&lt;td id="slot1"&gt;&lt;/td&gt;&lt;td
id="slot2"&gt;&lt;/td&gt;
57.				&lt;/tr&gt;
58.			&lt;/table&gt;
59.		&lt;/body&gt;
60.	&lt;/html&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>2. 主类HelloWorld</p>
		<p>主类HelloWorld必须继承自com.google.gwt.core.client.EntryPoint类，并且覆盖其public void onModuleLoad()方法。</p>
		<p>在onModuleLoad()方法中，我们使用GWT中提供的默认显示组件(比如Button)和组装组件(比如VerticalPanel)来设计需要显示的界面，所有这些组件都位于com.google.gwt.user.client.ui包下面。这些组件的命名规则和Java AWT的命名规则基本类似，使用方式也和AWT的基本一致，不过GWT中提供了不少的新组建可供选择，比如可以直接用VerticalPanel来实现垂直方向的布局管理。</p>
		<p>HelloWorld.java中的19和20行，就声明了Button和Label两个显示组件。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">19.    final Button button = new Button("Click me");
20.    final Label label = new Label();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>onModuleLoad()方法中，我们需要把生成的显示组件或者组装组建加入到显示页面的对应显示区域。首先，我们需要在页面上使用id属性为显示区域指定唯一的名称(参见清单1中的56行)。com.google.gwt.user.client.ui.RootPanel的静态方法get可以根据传入的字符串参数（参数值就是显示区域的id属性）获取页面上对应的显示区域。</p>
		<p>HelloWorld.java的36、37行，就使用了RootPanel的get方法分别获得页面上id为"slot1"、"slot2"的显示区域，然后调用add方法将生成的Button和Label对象加入到显示区域中。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">36.    RootPanel.get("slot1").add(button);
37.    RootPanel.get("slot2").add(label);
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>Ajax最重要的功能就是为用户提供更好的交互体验，GWT中使用和AWT中相同的事件监听机制完成客户端事件监听，GWT中支持的Listener接口包括ChangeListener、ClickListener等。我们可以通过各种各样的Listener接口获得用户界面上所发生的动作，通过处理这些动作来完成对应业务逻辑。</p>
		<p>HelloWorld.java中的第22行~29行的内容就实现了一个ClickListender接口，并且提供了对应的onClick方法实现，用于处理按钮点击后的业务逻辑（判断是否需要显示"Hello World!"字符串），然后调用Button对象的addClickListener方法增加监听器。</p>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">22.    button.addClickListener(new ClickListener() {
23.          public void onClick(Widget sender) {
24.            if (label.getText().equals(""))
25.              label.setText("Hello World!");
26.            else
27.              label.setText("");
28.          }
29.    });
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>HelloWorld类的全部源代码内容见清单2。</p>
		<br />
		<a name="N10289">
				<strong>清单2 src\org\vivianj\gwt\client\HelloWorld.java</strong>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">1.	package org.vivianj.gwt.client;
2.	
3.	import com.google.gwt.core.client.EntryPoint;
4.	import com.google.gwt.user.client.ui.Button;
5.	import com.google.gwt.user.client.ui.ClickListener;
6.	import com.google.gwt.user.client.ui.Label;
7.	import com.google.gwt.user.client.ui.RootPanel;
8.	import com.google.gwt.user.client.ui.Widget;
9.	
10.	/**
11.	 * Entry point classes define &lt;code&gt;onModuleLoad()&lt;/code&gt;.
12.	 */
13.	public class HelloWorld implements EntryPoint {
14.	
15.	  /**
16.	   * This is the entry point method.
17.	   */
18.	  public void onModuleLoad() {
19.	    final Button button = new Button("Click me");
20.	    final Label label = new Label();
21.	
22.	    button.addClickListener(new ClickListener() {
23.	      public void onClick(Widget sender) {
24.	        if (label.getText().equals(""))
25.	          label.setText("Hello World!");
26.	        else
27.	          label.setText("");
28.	      }
29.	    });
30.	
31.	    // Assume that the host HTML has elements defined whose
32.	    // IDs are "slot1", "slot2".  In a real app, you probably would not want
33.	    // to hard-code IDs.  Instead, you could, for example, search for all 
34.	    // elements with a particular CSS class and replace them with widgets.
35.	    //
36.	    RootPanel.get("slot1").add(button);
37.	    RootPanel.get("slot2").add(label);
38.	  }
39.	}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>3. 配置文件HelloWorld.gwt.xml</p>
		<p>配置文件中包括两个元素。Inherits元素地设置GWT应用的继承信息，而entry-point元素的class属性则用于设置设置GWT应用的主类。</p>
		<br />
		<a name="N10299">
				<strong>清单3 src\org\vivianj\gwt\HelloWorld.gwt.xml</strong>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">1.	&lt;module&gt;
2.	
3.		&lt;!-- Inherit the core Web Toolkit stuff.                  --&gt;
4.		&lt;inherits name='com.google.gwt.user.User'/&gt;
5.	
6.		&lt;!-- Specify the app entry point class.                   --&gt;
7.		&lt;entry-point class='org.vivianj.gwt.client.HelloWorld'/&gt;
8.	  
9.	&lt;/module&gt;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N102A3">
						<span class="atitle">
								<strong>
										<font size="4">结束语</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Ajax是当前热门的Web应用开发技术，Java是企业应用开发中的主流技术，GWT框架将这两种技术合二为一，是我们能够应用Java语言来开发Ajax，在保留Ajax技术的优点基础上，解决了Ajax应用开发、调试困难的缺点。</p>
		<p>本文中介绍了GWT的基本知识、主要特性，提供了"HelloWorld！"例子说明使用Java和GWT框架开发Ajax应用的开发、编译、测试过程，并且通过对"HelloWorld！"例子中涉及文件的分析，讲解了使用GWT框架开发Ajax应用的技术细节和约束条件。</p>
		<p>GWT 在线资源： <a href="http://code.google.com/webtoolkit/gettingstarted.html" target="_blank"><font color="#5c81a7">http://code.google.com/webtoolkit/gettingstarted.html</font></a></p>
		<p>GWT 下载地址：<a href="http://code.google.com/webtoolkit/" target="_blank"><font color="#5c81a7">http://code.google.com/webtoolkit/</font></a></p>
		<p>GWT 在线FAQ：<a href="http://code.google.com/webtoolkit/faq.html" target="_blank"><font color="#5c81a7">http://code.google.com/webtoolkit/faq.html</font></a></p>
		<p>GWT 开发者论坛：<a href="http://groups.google.com/group/Google-Web-Toolkit" target="_blank"><font color="#5c81a7">http://groups.google.com/group/Google-Web-Toolkit</font></a></p>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/56751.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-07-05 14:46 <a href="http://www.blogjava.net/chenleiyu/archive/2006/07/05/56751.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>当J2EE遇见了AJAX(Leo)</title><link>http://www.blogjava.net/chenleiyu/archive/2006/06/08/51400.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Thu, 08 Jun 2006 08:56:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/06/08/51400.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/51400.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/06/08/51400.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/51400.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/51400.html</trackback:ping><description><![CDATA[
		<p>以JSON格式传递数据 <br />JS里的主要内容:<br />var HTML_TABLE = "table";<br />var HTML_TBODY = "tbody";<br />var HTML_TR = "tr";<br />var HTML_TD = "td";<br />var HTML_A = "a";<br />var HTML_SELECT = "select";<br />var HTML_OPTION = "option";<br />var HTML_INPUT = "input";</p>
		<p>function table() {<br />    this.id = null;<br />    this.width = null;<br />    this.border = "0";<br />    this.className = null;<br />    this.cellspacing = 1;<br />    this.cellpadding = 1;<br /> this.bgcolor = null;<br /> <br /> this.position = null;<br /> this.zIndex = 0;<br /> this.left = null;<br /> this.top = null;<br /> <br />    var _tbody = null;<br />    <br />    this.setTbody = function setTbody(tbody) {<br />      _tbody = tbody;<br />    };<br />    <br />    this.getTbody = function getTbody(){<br />      return _tbody;<br />    };<br />    <br />    this.encodeToDom = function encodeToDom(docObj/*document??*/){<br />      var atable = docObj.createElement(HTML_TABLE);<br />      <br />   if(this.id != null){<br />     atable.setAttribute("id",this.id);<br />   }<br />   <br />   if(this.width!=null){<br />   atable.setAttribute("width",this.width);<br />   }</p>
		<p>   if(this.position != null){<br />     atable.style.position = this.position;<br />   }<br />   <br />   if(this.left != null){<br />    atable.style.left = this.left; <br />   }<br />   <br />   if(this.top != null){<br />    atable.style.top = this.top; <br />   }<br />   <br />   <br />   atable.style.zIndex = this.zIndex;<br />   <br />   if(this.bgcolor != null){<br />     atable.bgColor = this.bgcolor;<br />   }<br />   <br />   atable.setAttribute("border",this.border);<br />   if(docObj.uniqueID){<br />     atable.setAttribute("className",this.className);<br />   }else{<br />     atable.setAttribute("class",this.className);<br />   }<br />   atable.setAttribute("cellspacing",this.cellspacing);<br />   atable.setAttribute("cellpadding",this.cellpadding);</p>
		<p>   for(var j =0;j&lt;listeners.length;j++){<br />  if(docObj.uniqueID){<br />    atable.attachEvent(listeners[j].eventType,listeners[j].listenerFunction);<br />  }else{<br />    atable.addEventListener(listeners[j].eventType.substr(2),listeners[j].listenerFunction,true);<br />  }    <br />  <br />   }</p>
		<p>   if(_tbody != null){<br />     atable.appendChild(_tbody.encodeToDom(docObj));<br />   }<br />   <br />   return atable;<br />    };</p>
		<p>
				<br /> var listeners = new Array();<br />    this.attachEvent = function attachEvent(aeventType,alistenerFunction){<br />  var alistener = new listener(aeventType,alistenerFunction);<br />  listeners[listeners.length] = alistener;<br /> };</p>
		<p>}</p>
		<p>function tr() {<br />    this.id = null;<br />    this.className = null;<br />    this.title = "";<br />    <br />    var tds = new Array();<br />    this.appendTd = function appendTd(td){<br />      tds[tds.length] = td;<br />    };<br />    </p>
		<p>
				<br />    this.encodeToDom = function encodeToDom(docObj/*document??*/){<br />      var atr = docObj.createElement(HTML_TR);<br />      <br />      if(this.id != null){<br />        atr.setAttribute("id",this.id);<br />      }<br />   <br />   if(this.className != null){<br />     if(docObj.uniqueID){<br />       atr.setAttribute("className",this.className);<br />     }else{<br />       atr.setAttribute("class",this.className);<br />     }<br />   }<br />   <br />      atr.setAttribute("title",this.title);</p>
		<p>   for(var j =0;j&lt;listeners.length;j++){<br />  if(docObj.uniqueID){<br />    atr.attachEvent(listeners[j].eventType,listeners[j].listenerFunction);<br />  }else{<br />    atr.addEventListener(listeners[j].eventType.substr(2),listeners[j].listenerFunction,true);<br />  }    <br />  <br />   }</p>
		<p>      for(var i=0;i&lt;tds.length;i++){<br />        atr.appendChild(tds[i].encodeToDom(docObj));<br />      }<br />      <br />      return atr;<br />    };</p>
		<p> var listeners = new Array();<br />    this.attachEvent = function attachEvent(aeventType,alistenerFunction){<br />  var alistener = new listener(aeventType,alistenerFunction);<br />  listeners[listeners.length] = alistener;<br /> };</p>
		<p>}</p>
		<p>function td() {<br />    this.id = null;<br />    this.width = null;<br /> this.height = null;<br /> this.align = null;<br />    this.nowrap = false;<br />    this.className = null;<br /> <br /> this.onclick = null;<br /> <br />    <br />    var childObjs = new Array();<br />    this.appendChild = function appendChild(obj){<br />      childObjs[childObjs.length] = obj;<br />    };</p>
		<p>    this.encodeToDom = function encodeToDom(docObj/*document??*/){<br />      var atd = docObj.createElement(HTML_TD);<br />      <br />      if(this.id != null){<br />        atd.setAttribute("id",this.id);<br />      }<br />      <br />   atd.setAttribute("noWrap",this.nowrap);<br />   <br />   if(this.width!=null){<br />   atd.setAttribute("width",this.width);<br />   }<br />   <br />   if(this.height != null){<br />     atd.setAttribute("height",this.height);<br />   }<br />   <br />   if(this.align != null){<br />     atd.setAttribute("align",this.align);<br />   }   <br />      if(this.className != null){<br />     if(docObj.uniqueID){<br />       atd.setAttribute("className",this.className);<br />     }else{<br />       atd.setAttribute("class",this.className);<br />     }<br />   }<br />   atd.onclick = this.onclick;<br />   for(var j =0;j&lt;listeners.length;j++){<br />  if(docObj.uniqueID){<br />    atd.attachEvent(listeners[j].eventType,listeners[j].listenerFunction);<br />  }else{<br />    atd.addEventListener(listeners[j].eventType.substr(2),listeners[j].listenerFunction,true);<br />  }    <br />  <br />   }</p>
		<p>      <br />      for(var i=0;i&lt;childObjs.length;i++){<br />        atd.appendChild(childObjs[i].encodeToDom(docObj));<br />      }<br />      <br />      return atd;<br />    };</p>
		<p>
				<br /> var listeners = new Array();<br />    this.attachEvent = function attachEvent(aeventType,alistenerFunction){<br />  var alistener = new listener(aeventType,alistenerFunction);<br />  listeners[listeners.length] = alistener;<br /> };</p>
		<p>
				<br />}</p>
		<p>function text(content) {<br />    this.id = null;<br />    this.content = content;<br />    this.encodeToDom = function encodeToDom(docObj/*document??*/){<br />      return docObj.createTextNode(this.content);<br />    };<br />}<br />function tbody() {<br />    this.id = null;<br />    <br />    var trs = new Array();<br />    this.appendTr = function appendTr(tr){<br />      trs[trs.length] = tr;<br />    };<br />    <br />    <br />    this.encodeToDom = function encodeToDom(docObj/*document??*/){<br />      var atbody = docObj.createElement(HTML_TBODY);<br />      <br />      if(this.id != null){<br />        atbody.setAttribute("id",this.id);<br />      }<br />      <br />   for(var j =0;j&lt;listeners.length;j++){<br />  if(docObj.uniqueID){<br />    atbody.attachEvent(listeners[j].eventType,listeners[j].listenerFunction);<br />  }else{<br />    atbody.addEventListener(listeners[j].eventType.substr(2),listeners[j].listenerFunction,true);<br />  }    <br />  <br />   }</p>
		<p>
				<br />      for(var i = 0;i&lt;trs.length; i++){<br />        atbody.appendChild(trs[i].encodeToDom(docObj));<br />      }<br />      return atbody;<br />    };</p>
		<p>
				<br /> var listeners = new Array();<br />    this.attachEvent = function attachEvent(aeventType,alistenerFunction){<br />  var alistener = new listener(aeventType,alistenerFunction);<br />  listeners[listeners.length] = alistener;<br /> };</p>
		<p>}</p>
		<p>function createStatusTable(content){<br /> var _table = new table();<br /> <br /> _table.id = "_status";<br /> _table.bgcolor = "#CC0000";<br /> _table.cellspacing = "0";<br /> _table.cellpadding = "0";<br /> _table.position = "absolute";<br /> _table.left = 0;<br /> _table.top = 0;<br /> _table.zIndex = "3";<br /> _table.className = "white10Verdana";<br /> <br /> var _tbody = new tbody();<br /> _table.setTbody(_tbody);<br /> <br /> var _tr = new tr();<br /> _tbody.appendTr(_tr);<br /> <br /> var _td = new td();<br /> _td.id = "content"<br /> <br /> //_td.height = "30";<br /> _td.align = "center";<br /> _tr.appendTd(_td);<br /> <br /> var _text = new text(content);<br /> _td.appendChild(_text);<br /> return _table;<br />}<br />////<br />var xmlHttp;<br />var userNameParamValue;<br />var slotIdParamValue;<br />var jsonObject = null;<br />var accessParamValue;</p>
		<p>function conversate(requestURL,accessParamValue)<br />{<br /> try{<br /> createXmlHttp();<br /> xmlHttp.open("post",requestURL,true);<br /> xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");<br /> xmlHttp.onreadystatechange=handlestatechange;<br /> xmlHttp.send(null);<br /> }<br /> catch(e)<br /> {<br />  var tempTable = createStatusTable("Throw out some errors,error type:" + e.name + ",cause description:" + e.message);<br />   document.documentElement.getElementsByTagName("body")[0].appendChild(tempTable.encodeToDom(document));<br />   return;<br /> }<br />}</p>
		<p>function handlestatechange()<br />{<br />  if(document.getElementById("_status") == null){<br />      var tempTable = createStatusTable("...Requesting!");<br />   var mybody = document.documentElement.getElementsByTagName("body")[0];<br />   mybody.appendChild(tempTable.encodeToDom(document));<br /> }<br /> <br />    if(xmlHttp.readyState==4){<br />     if(xmlHttp.status==200){<br />    out("...Fetched data was successful!");<br />    executeTask();<br />  }else{<br />    out("Request fail:" + xmlHttp.status + "error?");<br />    return;<br />  }<br /> }else if(xmlHttp.readyState==0){<br />  out("...Connecting!");<br /> }else if(xmlHttp.readyState==1){<br />  out("...Loading!");<br /> }else if(xmlHttp.readyState==2){<br />  out("...Load end!");<br /> }else if(xmlHttp.readyState==3){<br />  out("...Processing!");<br /> }<br />}<br />function out(content){<br />  if(document.getElementById("_status") == null){<br />      var tempTable = createStatusTable("");<br />   var mybody = document.documentElement.getElementsByTagName("body")[0];<br />   mybody.appendChild(tempTable.encodeToDom(document));<br />  }else{<br />    document.getElementById("content").innerHTML = content;<br />  }<br />}</p>
		<p>
				<br />function executeTask(){<br />  try{<br />    var jsonString=xmlHttp.responseText;<br /> //alert("Response string is:"+jsonString);<br /> try{<br />   jsonObject=JSON.parse(jsonString);<br /> }catch(e){<br />  out("JSON object throw out some errors,error type:"+ e.name +",cause description:" + e.message);<br />  return;<br /> }<br /> switch(jsonObject.result){<br />   case 3:<br />     alert("You have no right to execute this request!");<br />  return false;<br />  break;<br />   case 2:<br />   alert("You are forced to quit by admin!");<br />  top.location="/index.html";<br />  break;<br />   case 1:<br />     out("The request executed failed! ");<br />  return;<br />  break;<br />   case 0:<br />     switch(accessParamValue){<br />    case 0:<br />      queryProcess();<br />      break;<br />    case 1:<br />      addProcess();<br />   break;<br />    case 2:<br />      deleteProcess();<br />   break;<br />    case 3:<br />      editProcess();<br />   break;<br />    case 4:<br />      enableRender();<br />      break;<br />    case 5:<br />      disableRender();<br />      break;<br />    case 6:<br />      modifyPasswordRender();<br />      break;<br />    case 7:<br />      forceQuitRender();<br />      break;<br />  }<br />  break;<br />   default:<br />     out("Response was wrong!");<br />     break;<br /> }<br /> <br /> out("...Request was completed!");<br /> window.setTimeout("wait()",800);<br /> <br />  }catch(e){<br />    out("...Render user interface failing! error type:" + e.name + ", cause:" + e.message);<br />  } </p>
		<p>}<br />//<br />function createXmlHttp(){<br />  if(xmlHttp == null){<br />   if(window.ActiveXObject){<br />       xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");<br />   }else if(window.XMLHttpRequest){<br />       xmlHttp=new XMLHttpRequest();<br />   }<br />  }<br />}<br />//<br />function changeFormElementsDisabledStatusTo(status){<br /> var allInputElements=document.getElementsByTagName("INPUT");<br /> var allSelectElements=document.getElementsByTagName("SELECT");<br /> var allTextareaElements=document.getElementsByTagName("TEXTAREA");<br /> for(var i=0;i&lt;allInputElements.length;i++){<br />  allInputElements[i].disabled=status;<br /> }<br /> for(var m=0;m&lt;allSelectElements.length;m++){<br />  allSelectElements[m].disabled=status;<br /> }<br /> for(var n=0;n&lt;allTextareaElements.length;n++){<br />  allTextareaElements[n].disabled=status;<br /> }<br />}<br />function wait(){<br /> if(document.getElementById("_status") != null)<br />   document.getElementById("_status").parentNode.removeChild(document.getElementById("_status"));<br />}<br /><br /><br />SERVLET里的内容:<br />  JSONObject jsonObject = new JSONObject();<br />  try {<br />   jsonObject.put("result",0);<br />   jsonObject.put("accessParamValue",0);<br />   <br />  } catch (JSONException e) {<br />   // TODO Auto-generated catch block<br />   e.printStackTrace();<br />  }<br />  PrintWriter out = response.getWriter();<br />        System.out.println("hhhhhhhhhh---&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;");<br />        out.write(jsonObject.toString());<br /><br />MD AJAX看起来华丽 做起来你就知道该死的JAVASCRIPT有多痛苦了 特别是调试的时候 哭吧<br /></p>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/51400.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-06-08 16:56 <a href="http://www.blogjava.net/chenleiyu/archive/2006/06/08/51400.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)看看如何在Struts应用中施展AJAX魔法</title><link>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45184.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 09 May 2006 04:39:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45184.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/45184.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/45184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/45184.html</trackback:ping><description><![CDATA[
		<p style="text-indent: 2em;">AJAX是最近在web开发流程中一次革命，它允许在一个普通的web浏览器中配置大量的动
态效果。多年前Struts
都已经是Java-Web开发中的一个事实上的标准，在大量的应用中都已经使用了它。本文将告诉你，如何在你已经存在Struts应用中结合丰富的
AJAX没Ы涌凇?</p>
		<p style="text-indent: 2em;">本文将介绍在JSP页面中包含一些JavaScript这样一个简单而有效的方法来结合
AJAX和Struts。在此我们介绍如何重新使用已经存在的Struts actions，但此技术同样可用于你选择的其他Java-Web
框架。本方法同样可平移到Struts或者JSF的下一个版本。</p>
		<p style="text-indent: 2em;">版权声明：任何获得授权的网站，转载时请务必保留以下作者信息和链接</p>
		<p style="text-indent: 2em;">作者:Paul Browne;pawenwen(作者的blog:http://blog.matrix.org.cn/page/pawenwen)</p>
		<p style="text-indent: 2em;">原文:原文链接</p>
		<p style="text-indent: 2em;">关键字:Struts;AJAX</p>
		<p style="text-indent: 2em;">
				<b>什么是AJAX？</b>
		</p>
		<p style="text-indent: 2em;">AJAX是“异步的JavaScript和XML”的缩写。这是一项技术，而不是一个如
Struts一样的框架。为什么在AJAX周围会有这么多的关注呢？这是因为AJAX使web页面看起来并不像一个平面的文档，而更像用户所期望的如桌面
应用的动态GUI应用程序。AJAX技术能在很多的浏览器上使用（包括IE和Netscape/Mozilla）。它已经为Microsoft（用于
Outlook的web客户端）和Google（用户Google Maps和Gmail）所使用。</p>
		<p style="text-indent: 2em;">
				<b>未使用AJAX之前</b>
		</p>
		<p style="text-indent: 2em;">目前大多数的Struts应用都是标准的“如同一个平面文档的web页面”的结构。如果你想
模仿一些桌面应用程序（比如那些使用Java Swing，Visual
Basic，或者Delphi建立的应用程序），那么你有两个选择：你可以发送所有的可能作为页面的一部分被请求的信息，使用大量的JavaScript
来操作其动态的显示（一个很慢并且非企业级Java的方法）；或者你可以不改变形式地提交到后台服务器（一种有效的方法）
。AJAX提高给你了融合前面的最佳解决方案：动态的页面，但是大多数的应用是在你的web服务器的Java程序来处理的。</p>
		<p style="text-indent: 2em;">
				<b>AJAX 101</b>
		</p>
		<p style="text-indent: 2em;">AJAX和现有的动态HTML技术非常相似，并在其上增加了一个发送到“后台”服务器的请求来获取需要的新的或者更新的信息。AJAX的机制在其他地方已经有详细的说明――请查看本文后的Resources来获取更多。但是你至少需要知道：</p>
		<p style="text-indent: 2em;">1. XMLHttpRequest
（如果你使用的是IE浏览器的话，则是Microsoft.XMLHTTP的ActiveX的对象）。这些对象，你可以在web页面中使用
JavaScript调用。他们允许你作为后台的调用方式来请求web服务器的内容（例如，在表单提交后，屏幕并不像平时一样显示“空白”）。</p>
		<p style="text-indent: 2em;">2. XMLHttpRequest 和Microsoft.XMLHTTP 返回的内容可以作为XML或者文本来处理。JavaScript（在你页面上的）可以使用请求的新内容来更新页面。</p>
		<p style="text-indent: 2em;">3. 整个处理过程可以由普通的JavaScript事件来触发：onclick，onchange，onblur，等。 </p>
		<p style="text-indent: 2em;">
				<b>在你的Struts应用中使用AJAX</b>
		</p>
		<p style="text-indent: 2em;">你阅读了本文，然后你会对使用AJAX来创建动态的web页面感兴趣，并且想知道如何将它加入到你的Struts应用中。这只是选择之一。那么你会如何选择呢？</p>
		<p style="text-indent: 2em;">· 等待，知道Struts的下一个版本融合了AJAX技术。如果Struts开发者准备开发一个新的应用，这或许会是一个好的选择。再后面，可能会是要求JSF也这样做――对其本身并不一件坏事，但是对于已经存在的系统这将会带来最根本的改变。</p>
		<p style="text-indent: 2em;">· 你也可以直接采用新的方法，比如Direct Web Remoting (DWR)
和Ruby on
Rails，这些都是专为建立AJAX应用的。如果你考虑不使用Struts来开发web的话，那么这些都是很又用的框架，并且值得一试。但是，这也同时
意味着你必须要重写你的应用。</p>
		<p style="text-indent: 2em;">· 在你已经存在的Struts应用中增加AJAX。既然AJAX只是一项技术而并非框架，那么它就很容易融入到Struts中。作为现有的系统，其稳定性（如，保持现有的库文件）是相当重要的。所以这个方法被推荐，并且我们将在后面详细介绍。 </p>
		<p style="text-indent: 2em;">我们选择在Struts应用中增加AJAX的优势是：</p>
		<p style="text-indent: 2em;">1. 它并不需要任何新的库文件或者服务器代码；只需要使用现有的Struts库文件和action。 </p>
		<p style="text-indent: 2em;">2. 解决方案中所有部分――JavaScript，XML，Java和Struts――早已为广泛所知。 </p>
		<p style="text-indent: 2em;">3. 此应用可以一块一块地移植到AJAX；我们可以确定哪些部分对用户有益，并首先将它们更新到动态AJAX显示。 </p>
		<p style="text-indent: 2em;">实现方案</p>
		<p style="text-indent: 2em;">我们如何真正的贯彻我们的选择呢？我们首先应该注意一个“标准的”（没有AJAX）Struts应用是如何工作的。在此应用中，一个一般的事件流程如下：</p>
		<p style="text-indent: 2em;">1. 使用点击超链接或者表单的提交按钮，用户发送请求。</p>
		<p style="text-indent: 2em;">2. web服务器运行处理请求的Struts Action来生成一个web页面。 </p>
		<p style="text-indent: 2em;">3. 浏览器显示web页面。</p>
		<p style="text-indent: 2em;">4. 当用户点击保存的时候，信息由Struts框架中一个ActionForm类来转换并发送到服务器。</p>
		<p style="text-indent: 2em;">5. 然后，Struts框架调用Struts Action来出来请求（如，保存数据到数据库中）。 </p>
		<p style="text-indent: 2em;">6. 页面再一次回传，处理流程继续。</p>
		<p style="text-indent: 2em;">
				<b>现有的Struts应用</b>
		</p>
		<p style="text-indent: 2em;">一个演示事件流程的简单Struts应用可以在以下地址下载：
struts-non-ajax.zip。此基于Struts的应用，是基于用户的输入显示或者隐藏蓝色和绿色的表格。图1显示了载入初始页面的画面。图
2显示了用户输入值并点击了提交后的画面。虽然简单，但它已经足以表示一个Struts的工作流程。</p>
		<p style="text-indent: 2em;">
		</p>
		<center>
				<img src="http://cimg2.163.com/catchpic/3/3E/3EF19C5151FDAA0B6631BBB517C9F1F0.jpg" border="0" />
		</center>
		<p style="text-indent: 2em;">图 1. 没有AJAX的例子：初始屏幕</p>
		<p style="text-indent: 2em;">
		</p>
		<center>
				<img src="http://cimg2.163.com/catchpic/8/8E/8EBF4FE738024C993210D15E72ECFBEF.jpg" border="0" />
		</center>
		<p style="text-indent: 2em;">图 2. 没有AJAX的例子：输入值并点击了提交</p>
		<p style="text-indent: 2em;">服务器端的代码是：一个Struts Action使用struts-config.xml 中定义的值转发到（相同的）JSP。这个例子代码中一些需要注意的地方是：</p>
		<p style="text-indent: 2em;">·struts-config.xml文件将所有的请求重定向到http://localhost:8080/struts-non-ajax/（或者和你自己的服务器相同）的index.jsp。 </p>
		<p style="text-indent: 2em;">· index.jsp 包含了一个两个文本框的Struts form（showBlue和showGreen）。该页面同样包含了标签，但是如同两个文本框被初始化为空，标签之间的内容并不显示。 </p>
		<p style="text-indent: 2em;">· 用户输入值（true或者false）并点击提交按钮，处理控制（经过Struts框架，读取struts-config.xml）提交到SampleAction类中。 </p>
		<p style="text-indent: 2em;">·SampleAction记录下值，然后转发到index.jsp。一个成熟的Struts应用可能会处理更多的事情，不如保存或者查询数据库等。</p>
		<p style="text-indent: 2em;">· index.jsp 现在重新处理请求；如果ShowBlue或者ShowGreen的值是true，这些表格就显示出来。 </p>
		<p style="text-indent: 2em;">该应用并没有任何“错误”。类似的Struts项目好多年都是这样做的。但是，我们如何在不添加复杂的JavaScript或者频繁的表单提交的前提下，为此应用增加动态的元素呢？</p>
		<p style="text-indent: 2em;">
				<strong>我们的第一个Struts AJAX应用
</strong>
		</p>
		<p style="text-indent: 2em;">观察下下面的图3和图4。第一眼看上去，它们和前面的例子没有说明区别。它们的不同之处在于，页面载入后（图3）然后文本框中的值改变了，窗体自动提交而不显示空白的，然后在图4中显示结果。普通的提交按钮仍然在，你也可以选择使用它。</p>
		<p style="text-indent: 2em;">
		</p>
		<center>
				<img src="http://cimg2.163.com/catchpic/D/DE/DE96EE3544D3381BD44F72923C92F9E9.jpg" border="0" />
		</center>
		<p style="text-indent: 2em;">图 3. 页面载入后的AJAX例子</p>
		<p style="text-indent: 2em;">
		</p>
		<center>
				<img src="http://cimg2.163.com/catchpic/1/18/18F3326EA4DEBDE76FC598E103031400.jpg" border="0" />
		</center>
		<p style="text-indent: 2em;">图 4. AJAX调用后的AJAX例子</p>
		<p style="text-indent: 2em;">添加AJAX是出奇的容易。服务器端的代码和前面的例子是一样的： 一个Struts的ActionForm来后去数据，一个Struts的Action来执行需要的任务（例如，存储数据库）然后转发到适当的JSP页面来显示结果。</p>
		<p style="text-indent: 2em;">继续</p>
		<p style="text-indent: 2em;">如果你希望就此停止阅读（跳过这个例子的工作说明），但是这里的是和你需要转换你的Struts应用到一个Struts－AJAX应用同样的风格： </p>
		<p style="text-indent: 2em;">1. 在你的web页面中引入一个Ajax.js （该文件是struts-ajax.zip 例文件中的一部分）。Ajax.js 包含了所有需要发送和接收AJAX调用的JavaScript方法。</p>
		<p style="text-indent: 2em;">2. 确保你希望在AJAX调用中更新的web页面的部分包含在标签中，并且给每个标签一个id。 </p>
		<p style="text-indent: 2em;">3. 当一些事件触发的时候就更新页面（例如，文本框的the onchange()方法），调用retrieveURL()方法，通过URL传递到需要执行服务器端处理的Struts Action。</p>
		<p style="text-indent: 2em;">4. 为了页面的显示/更新，最简单的方法是Struts Action转发回同样的页面。在本例中，showGreen/showBlue 文本框中的onchange()方法来触发AJAX调用。 </p>
		<p style="text-indent: 2em;">JavaScript方法retrieveURL()调用服务器的Struts（通过URL），获取JSP响应，然后更新显示页面中的 标签中的部分。就是这么简单！</p>
		<p style="text-indent: 2em;">
				<b>AJAX解决方案的细节</b>
		</p>
		<p style="text-indent: 2em;">我们将例子变为AJAX－Struts应用的时候，需要三个变化： </p>
		<p style="text-indent: 2em;">1. 增加一个JavaScript方法来完成到服务器的“背后的”AJAX调用。</p>
		<p style="text-indent: 2em;">2. 增加JavaScript代码来接收服务器的响应并更新页面。 </p>
		<p style="text-indent: 2em;">3. 在JSP页面增加标签标签，这个标签中内容将在AJAX调用中更新。 </p>
		<p style="text-indent: 2em;">我们将详细的说明上面的每一步。</p>
		<p style="text-indent: 2em;">
				<b>发送AJAX请求到服务器</b>
		</p>
		<p style="text-indent: 2em;">有两个方法（在下面列出）用于发送请求到服务器。 </p>
		<p style="text-indent: 2em;">· retrieveURL()方法获得服务器的URL和Struts form。URL用于使用AJAX，form的值用于传递到服务器。 </p>
		<p style="text-indent: 2em;">· getFormAsString()方法用于将retrieveURL()中form命名的值组装成查询字符串，并发送到服务器。 </p>
		<p style="text-indent: 2em;">使用方法很简单，使用onclick()/onChange()事件来触发retrieveURL()更新显示。</p>
		<p style="text-indent: 2em;">在这两个方法中有一些有趣的东西。</p>
		<p style="text-indent: 2em;">在retrieveURL()方法中，req.onreadystatechange =
processStateChange
（注意，没有括号）这一行来告诉浏览器在服务器响应到达的时候调用processStateChange()方法（该方法将在后面介绍）。
retrieveURL()方法中（现在已经是AJAX的标准了）同样决定是使用IE浏览器(ActiveX)还是使用Netscape/Mozilla
(XmlHttpRequest) 来实现跨浏览器兼容。</p>
		<p style="text-indent: 2em;">getFormAsString()方法将HTML
form转换成字符串连接在URL后面（这样就允许我们发送HTTP
GET请求）。这个字符串是经过转换的（比如，空格转换成%20等），并且是一个Struts能将其组装成ActionForm的格式（并不需要
Struts清楚的明白这个是来之AJAX的请求）。注意，在本例中我们使用HTTP GET，使用HTTP POST的方法也是类似的。</p>
		<p style="text-indent: 2em;">function retrieveURL(url,nameOfFormToPost) {</p>
		<p style="text-indent: 2em;">//将url转换成字符串</p>
		<p style="text-indent: 2em;">url=url+getFormAsString(nameOfFormToPost);</p>
		<p style="text-indent: 2em;">//调用AJAX</p>
		<p style="text-indent: 2em;">if (window.XMLHttpRequest) { </p>
		<p style="text-indent: 2em;">// 非IE浏览器</p>
		<p style="text-indent: 2em;">req = new XMLHttpRequest();</p>
		<p style="text-indent: 2em;">req.onreadystatechange = processStateChange;</p>
		<p style="text-indent: 2em;">try {</p>
		<p style="text-indent: 2em;">req.open("GET", url, true); </p>
		<p style="text-indent: 2em;">} catch (e) {</p>
		<p style="text-indent: 2em;">alert("Server Communication Problem\n"+e);</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">req.send(null);</p>
		<p style="text-indent: 2em;">} else if (window.ActiveXObject) {</p>
		<p style="text-indent: 2em;">// IE</p>
		<p style="text-indent: 2em;">req = new ActiveXObject("Microsoft.XMLHTTP");</p>
		<p style="text-indent: 2em;">if (req) {</p>
		<p style="text-indent: 2em;">req.onreadystatechange=processStateChange;</p>
		<p style="text-indent: 2em;">req.open("GET", url, true);</p>
		<p style="text-indent: 2em;">req.send();</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">getFormAsString() 是一个“私有” 方法，在retrieveURL()中使用。</p>
		<p style="text-indent: 2em;">function getFormAsString(formName){</p>
		<p style="text-indent: 2em;">//设置返回字符串</p>
		<p style="text-indent: 2em;">returnString ="";</p>
		<p style="text-indent: 2em;">//取得表单的值</p>
		<p style="text-indent: 2em;">formElements=document.forms[formName].elements;</p>
		<p style="text-indent: 2em;">//循环数组，组装url</p>
		<p style="text-indent: 2em;">//像'/strutsaction.do&amp;name=value'这样的格式</p>
		<p style="text-indent: 2em;">for(var i=formElements.length-1;i&gt;=0; --i ){</p>
		<p style="text-indent: 2em;">//转化每一个值</p>
		<p style="text-indent: 2em;">returnString+="&amp;" </p>
		<p style="text-indent: 2em;">+escape(formElements[i].name)+"=" </p>
		<p style="text-indent: 2em;">+escape(formElements[i].value);</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">//返回字符串</p>
		<p style="text-indent: 2em;">return returnString; </p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">
				<b>根据AJAX的响应更新web页面</b>
		</p>
		<p style="text-indent: 2em;">到现在为止，我们学习过了使用JavaScript来完成AJAX调用（前面列出），
Struts
Action，ActionForm以及JSP（基本没有变化，只是增加了标签）。为了完善我们对Struts－AJAX项目的了解，我们需要了解三个用
于根据服务器返回的结果而更新页面的JavaScript方法。</p>
		<p style="text-indent: 2em;">· processStateChange(): 该方法在AJAX调用前设定。它在服务器响应到达后由XMLHttpRequest/Microsoft.XMLHTTP 对象调用。 </p>
		<p style="text-indent: 2em;">·splitTextIntoSpan(): 根据响应，循环取出数组中的元素组装成NewContent。</p>
		<p style="text-indent: 2em;">·replaceExistingWithNewHtml():
根据span元素数组，循环搜索，将里面的元素调换掉页面中和它的'someName'相同的中的内容。注意，我们使用的是
req.responseText 方法来获得返回的内容（它允许我们操作任何文本的响应）。与此相对于的是req.responseXml
（它的作用更大，但是要求服务器返回是XHTML或者XML）。 </p>
		<p style="text-indent: 2em;">function processStateChange() {</p>
		<p style="text-indent: 2em;">if (req.readyState == 4) { // 完成</p>
		<p style="text-indent: 2em;">if (req.status == 200) { // 响应正常</p>
		<p style="text-indent: 2em;">//将响应的文本分割成Span元素</p>
		<p style="text-indent: 2em;">spanElements = </p>
		<p style="text-indent: 2em;">splitTextIntoSpan(req.responseText);</p>
		<p style="text-indent: 2em;">//使用这些Span元素更新页面</p>
		<p style="text-indent: 2em;">replaceExistingWithNewHtml(spanElements);</p>
		<p style="text-indent: 2em;">} else {</p>
		<p style="text-indent: 2em;">alert("Problem with server response:\n " </p>
		<p style="text-indent: 2em;">+ req.statusText);</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">replaceExistingWithNewHtml() 是为processStateChange()使用的“私有”方法。</p>
		<p style="text-indent: 2em;">function replaceExistingWithNewHtml </p>
		<p style="text-indent: 2em;">(newTextElements){</p>
		<p style="text-indent: 2em;">//循环newTextElements</p>
		<p style="text-indent: 2em;">for(var i=newTextElements.length-1;i&gt;=0;--i){</p>
		<p style="text-indent: 2em;">//判断是否以 if(newTextElements[i]. </p>
		<p style="text-indent: 2em;">indexOf("-1){</p>
		<p style="text-indent: 2em;">//获得span的名字- 设置在第一和第二个引号之间</p>
		<p style="text-indent: 2em;">//确认span元素是以下的格式</p>
		<p style="text-indent: 2em;">//NewContent</p>
		<p style="text-indent: 2em;">startNamePos=newTextElements[i]. </p>
		<p style="text-indent: 2em;">indexOf('"')+1;</p>
		<p style="text-indent: 2em;">endNamePos=newTextElements[i]. </p>
		<p style="text-indent: 2em;">indexOf('"',startNamePos);</p>
		<p style="text-indent: 2em;">name=newTextElements[i]. </p>
		<p style="text-indent: 2em;">substring(startNamePos,endNamePos);</p>
		<p style="text-indent: 2em;">//获得内容－在第一个&gt;标记后的所有内容</p>
		<p style="text-indent: 2em;">startContentPos=newTextElements[i]. </p>
		<p style="text-indent: 2em;">indexOf('&gt;')+1; </p>
		<p style="text-indent: 2em;">content=newTextElements[i].</p>
		<p style="text-indent: 2em;">substring(startContentPos);</p>
		<p style="text-indent: 2em;">//现在更新现有的Document中的元素， </p>
		<p style="text-indent: 2em;">// 确保文档存在该元素</p>
		<p style="text-indent: 2em;">if(document.getElementById(name)){</p>
		<p style="text-indent: 2em;">document.getElementById(name). </p>
		<p style="text-indent: 2em;">innerHTML = content;</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">splitTextIntoSpan() 是为processStateChange() 使用的“私有”方法。</p>
		<p style="text-indent: 2em;">function splitTextIntoSpan(textToSplit){</p>
		<p style="text-indent: 2em;">//分割文档</p>
		<p style="text-indent: 2em;">returnElements=textToSplit. </p>
		<p style="text-indent: 2em;">split("")</p>
		<p style="text-indent: 2em;">//处理每个元素 </p>
		<p style="text-indent: 2em;">for(var i=returnElements.length-1;i&gt;=0;--i){</p>
		<p style="text-indent: 2em;">//删除掉第一个span后面的元素</p>
		<p style="text-indent: 2em;">spanPos = returnElements[i]. </p>
		<p style="text-indent: 2em;">indexOf(" </p>
		<p style="text-indent: 2em;">//如果找到匹配的，获得span前的内容</p>
		<p style="text-indent: 2em;">if(spanPos&gt;0){</p>
		<p style="text-indent: 2em;">subString=returnElements[i].</p>
		<p style="text-indent: 2em;">substring(spanPos);</p>
		<p style="text-indent: 2em;">returnElements[i]=subString;</p>
		<p style="text-indent: 2em;">} </p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">return returnElements;</p>
		<p style="text-indent: 2em;">}</p>
		<p style="text-indent: 2em;">
		</p>
		<p style="text-indent: 2em;">添加以下的JavaScript代码到我们的应用中，以下的步骤将在服务器和浏览器中执行。</p>
		<p style="text-indent: 2em;">1. 如同一个普通Struts应用装载页面。</p>
		<p style="text-indent: 2em;">2. 用户改变文本框的值，触发一个onChange() 事件，调用retrieveURL() 方法。 </p>
		<p style="text-indent: 2em;">3. 该JavaScript方法通过发送Struts明白的表单变量（后台）请求到服务器的Struts Action。</p>
		<p style="text-indent: 2em;">4. 该JavaScript方法同样设定了第二个JavaScript方法的名字，此方法将到服务器响应完毕后调用。本例子中，设定为processStateChange() 方法。 </p>
		<p style="text-indent: 2em;">5. 如我们所预期的，服务器响应完毕，调用processStateChange() 方法。 </p>
		<p style="text-indent: 2em;">6. JavaScript在（新的）服务器响应中循环取出所有元素。将页面上存在与获得元素名字相同的 中的元素替换掉。 </p>
		<p style="text-indent: 2em;">
				<b>在你的应用中设计AJAX</b>
		</p>
		<p style="text-indent: 2em;">以上描述的JavaScript方法能在大多数的应用中使用，包括比我们的例子复杂得多的。但是，在使用之前，你需要注意以下几点：</p>
		<p style="text-indent: 2em;">· 避免复制代码，最好在初始化请求（如，显示完整的页面）和AJAX（更新部分页面）请求中使用相同的Struts Action和JSP。 </p>
		<p style="text-indent: 2em;">·在公共的Action（控制器）中，决定JSP页面（所有的JSP页面或者其中的一部分）中的一个区域需要传送到浏览器。通过在web服务器的session或者ActionForm中设定标记来让JSP页面知道哪些部分需要提交。</p>
		<p style="text-indent: 2em;">· 在JSP中，使用Struts 或者JSTL标签来决定提交的HTML区域。</p>
		<p style="text-indent: 2em;">使用AJAX的本例子，可以在以下下载： struts-Ajax.zip</p>
		<p style="text-indent: 2em;">
				<b>结语</b>
		</p>
		<p style="text-indent: 2em;">AJAX技术允许我们在创建和使用web应用的时候完全的改变。本文介绍了一个简单的技术，
在现有的Struts应用中增加Struts的处理。它允许我们利用我们已有的东西，不仅仅是代码，还包括了开发的技能。作为一个好的产品，它同样允许我
们写出更清晰，更具移植性的Java Struts应用。</p>
		<p style="text-indent: 2em;">
		</p>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/45184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-05-09 12:39 <a href="http://www.blogjava.net/chenleiyu/archive/2006/05/09/45184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)Ajax in Struts初试</title><link>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45168.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 09 May 2006 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45168.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/45168.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/05/09/45168.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/45168.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/45168.html</trackback:ping><description><![CDATA[
		<img title="" style="" alt="" src="http://www.blueclassic.net/upload/sun.gif" onload="javascript:if(this.width&gt;400)this.width=400;this.style.cursor = &quot;hand&quot;;this.onclick=Function(&quot;window.open(this.src)&quot;);" width="400" />
		<br />近段时间一直在研究Ajax和Struts的应用，苦于没有高手指导,只能独自一人一步步的尝试，可能转了不少的圈。<br />此文主要是解释一下我是怎么利用Ajax在Struts上的应用,有不足的地方高手指点。<br />这里建立一种基于Struts的Web应用，由于Struts没有AJAX支持,必须自己增加。而由于AJAX是没有框架而是动态使用Javascript和Dhtml,它很容易与Struts的组合。<br />相关代码如下....<br /><p class="code">...<br />&lt;tr&gt;<br />  &lt;td&gt;&lt;label for="first"&gt;&lt;bean:message key="nl.company.first"/&gt;&lt;/label&gt;&lt;/td&gt;<br />  &lt;td&gt;<br />    &lt;!--On change the function retrieveSecondOptions() is called to populate the second box --&gt;<br />    &lt;html:select property="first" onchange="retrieveSecondOptions()" styleId="firstBox" styleClass="mandatory"&gt;<br />      &lt;html:options collection="firstOptions" property="value" labelProperty="label"/&gt;<br />    &lt;/html:select&gt;    <br />  &lt;/td&gt;<br />&lt;/tr&gt;<br />&lt;tr&gt;<br />  &lt;td&gt;&lt;label for="second"&gt;&lt;bean:message key="nl.company.second"/&gt;&lt;/label&gt;&lt;/td&gt;<br />  &lt;td&gt;<br />    &lt;html:select property="second" styleId="secondBox" styleClass="mandatory"&gt;<br />      &lt;html:option value="nothing"&gt;-First choose above-&lt;/html:option&gt;<br />    &lt;/html:select&gt;    <br />  &lt;/td&gt;<br />&lt;/tr&gt;<br />...<br /></p><b>Javascript 
如下:</b><br /><p class="code">&lt;script lang="javascript"&gt;<br /> var req;<br /> /*<br />  * 由第二个options得到Struts动作<br />  */<br /> function retrieveRassen(){<br />    <br />    firstBox = document.getElementById('firstBox');<br />    <br />    //Nothing selected<br />    if(firstBox.selectedIndex==0){<br />      return;<br />    }<br />    selectedOption = firstBox.options[firstBox.selectedIndex].value;<br />    url="retrieveSecondOptionsAjaxAction.do?selectedOption="+selectedOption;<br /><br />    //Do the Ajax call<br />    if (window.XMLHttpRequest){ // Non-IE browsers<br />      req = new XMLHttpRequest();<br />      req.onreadystatechange = populateRassen;<br />      try {<br />        req.open("GET", url, true); //was get<br />      } catch (e) {<br />         alert("Cannot connect to server);<br />      }<br />      req.send(null);<br />    } else if (window.ActiveXObject) { // IE      <br />      req = new ActiveXObject("Microsoft.XMLHTTP");<br />      if (req) {<br />        req.onreadystatechange = populateSecondBox;<br />        req.open("GET", url, true);<br />        req.send();<br />      }<br />    }<br />  }<br />  <br />  //Callback function<br />  function populateSecondBox(){<br />    document.getElementById('rassen').options.length = 0;<br /><br />    if (req.readyState == 4) { // Complete<br />      if (req.status == 200) { // OK response<br />         textToSplit = req.responseText<br />         if(textToSplit == '803'){alert("No select option available on the server")}<br />          //Split the document<br />          returnElements=textToSplit.split("||")<br />          <br />          //Process each of the elements   <br />          for ( var i=0; i&lt;returnelements .length; i++ ){<br />             valueLabelPair = returnElements[i].split("|")<br />             document.getElementById('secondBox').options[i] = new Option(valueLabelPair[0], valueLabelPair[1]);<br />          }<br />        }<br />      } else {  <br />            alert("Bad response by the server");<br />        }    <br />    }<br />    }<br />&lt;/script&gt;<br /></p>接下来必须实施Struts行为..<br /><p class="code">package nl.company.action;<br /><br />import org.apache.log4j.Logger;<br /><br />import org.apache.struts.action.Action;<br />import org.apache.struts.action.ActionError;<br />import org.apache.struts.action.ActionErrors;<br />import org.apache.struts.action.ActionForm;<br />import org.apache.struts.action.ActionForward;<br />import org.apache.struts.action.ActionMapping;<br /><br />import nl.company.*<br />import java.io.*<br />import java.util.List;<br /><br />import javax.servlet.ServletException;<br />import javax.servlet.http.HttpServletRequest;<br />import javax.servlet.http.HttpServletResponse;<br /><br />public class RetrieveSecondOptionsAjaxAction extends Action<br />  {<br />    public ActionForward execute(ActionMapping mapping, ActionForm form,<br />        HttpServletRequest request, HttpServletResponse response)<br />        throws IOException, ServletException<br />      {<br />        Logger logger = Logger.getLogger(getClass());<br />        logger.info(<br />            "==========================================================");<br />        logger.info("Starting in RetrieveSecondOptionsAjaxAction");<br /><br />        String optionSelected = request.getParameter("optionSelected");<br /><br />        //Check of het soortId wel correct is<br />        if (ValidationSupport.isEmptyString(selectedOption))<br />          {<br />            logger.debug("No selected option supplied");<br /><br />            PrintWriter out = response.getWriter();<br />            out.print("803");<br />          }<br />        else<br />          {<br />            List options = getSecondOptions(selectedOption);<br />            String outLine = makeOutputString(options);<br />            out.print(outLine);     <br />          }<br />      }<br />      return null;<br />  }<br /></p>这里是个比较简单的Ajax在Struts上的应用，不过还是调试了关天才看到结果。汗<img src ="http://www.blogjava.net/chenleiyu/aggbug/45168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-05-09 11:09 <a href="http://www.blogjava.net/chenleiyu/archive/2006/05/09/45168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)DWR让AJAX如此简单</title><link>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43028.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 25 Apr 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43028.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/43028.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43028.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/43028.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/43028.html</trackback:ping><description><![CDATA[
		<b>
				<span style="font-size: 16px;">概述</span>
		</b>
		<br />
		<br />这篇文章阐述了使用开源项目DWR(直接Web远程控制)和AJAX(异步JavaScript和XML)的概念来提高Web应用的可用性。作者一步步来展示DWR如何使得AJAX的应用既简单又快捷。（1600字;2005年6月20日） <br /><br />AJAX，
或者说是异步JavaScript和XML，描述了一种使用混合了HTML(或XHTML)和层叠样式表作为表达信息,来创建交互式的Web应用的开发技
术;文档对象模型（DOM），JavaScript,动态地显示和与表达信息进行交互;并且，XMLHttpRequest对象与Web服务器异步地交换
和处理数据。<br /><br />因特网上许多例子展示了在一个HTML文件内部使用XMLHttpRequest与服务器端进行交互的必要的步骤。当手工地
编写和维护XMLHttpRequest代码时，开发者必须处理许多潜在的问题，特别是类似于跨浏览器的DOM实现的兼容性这样的问题。这将会导致在编码
和调试Javascript代码上面花费数不清的时间，这显然对开发者来说很不友好。<br /><br />DWR(直接Web远程控制)项目是在Apache
许可下的一个开源的解决方案，它供给那些想要以一种简单的方式使用AJAX和XMLHttpRequest的开发者。它具有一套Javascript功能
集，它们把从HTML页面调用应用服务器上的Java对象的方法简化了。它操控不同类型的参数，并同时保持了HTML代码的可读性。<br /><br />DWR
不是对一个设计的插入，也不强迫对象使用任何种类的继承结构。它和servlet框架内的应用配合的很好。对缺少DHTML编程经验的开发者来说，DWR
也提供了一个JavaScript库包含了经常使用的DHTML任务，如组装表，用item填充select下拉框，改变HTML元素的内容，如&lt;
div&gt;和&lt;span&gt;<br />DWR网站是详尽的并且有大量的文档，这也是这篇文章的基础。一些例子用来展示DWR如何使用和用它的库可以完成什么样的工作。<br /><br />这篇文章让读者看到了一个使用了DWR的Web应用是如何一步步建立的。我会展示创建这个简单的示例应用的必要的细节，这个应用是可下载的并且可以在你的环境中布署来看看DWR如何工作。<br />注意：找到有关AJAX的信息并不困难;网页上有几篇文章和博客的条目涵盖了这个主题，每一个都试图指出和评论这个概念的不同的方面。在资源部分，你会找到一些有趣的指向示例和文章的链接，来学习AJAX的更多的内容。<br /><br /><b><span style="font-size: 16px;">示例应用</span></b><br />这篇文章使用的示例应用模拟了多伦多的一个公寓出租搜索引擎。用户可以在搜索前选择一组搜索标准。为了提高交互性，AJAX中以下两种情况下使用：<br />·应用通告用户配合他的选择会返回多少搜索结果。这个数字是实时更新的-使用AJAX-当用户选择的卧室和浴室的数量，或者价格范围变化时。当符合标准的搜索结果没有或太多时，用户就没有必要点击搜索按纽。<br />·数据库查询并取回结果是由AJAX完成的。当用户按下显示结果按钮时，数据库执行搜索。这样，应用看起来更具响应了，而整个页面不需要重载来显示结果。<br /><br /><b><span style="font-size: 16px;">数据库</span></b><br />我们使用的数据库是HSQL，它是一种占用资源很小的Java SQL数据库引擎，可以不需要安装和配置的与Web应用捆绑在一起。一个SQL文件被用来在Web应用的上下文启动时创建一个内存中的表并添加一些记录。<br /><br /><b><span style="font-size: 16px;">Java类</span></b><br />应
用包含了两个主要的类叫Apartment和ApartmentDAO。Apartment.java类是一个有着属性和getter/setter方法
的简单的Java类。ApartmentDAO.java是数据访问类，用来查询数据库并基于用户的搜索标准来返回信息。ApartmentDAO类的实
现的直接了当的;它直接使用了Java数据库联接调用来得到公寓的总数和符合用户请求的可用公寓的列表。<br /><br /><b><span style="font-size: 16px;">DWR配置和使用</span></b><br />设
置DWR的使用是简单的：将DWR的jar文件拷入Web应用的WEB-INF/lib目录中，在web.xml中增加一个servlet声明，并创建
DWR的配置文件。DWR的分发中需要使用一个单独的jar文件。你必须将DWR
servlet加到应用的WEB-INF/web.xml中布署描述段中去。<br /><pre class="overflow">    &lt;servlet&gt;<br />        &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br />        &lt;display-name&gt;DWR Servlet&lt;/display-name&gt;<br />        &lt;description&gt;Direct Web Remoter Servlet&lt;/description&gt;<br />        &lt;servlet-class&gt;uk.ltd.getahead.dwr.DWRServlet&lt;/servlet-class&gt;<br />        &lt;init-param&gt;<br />            &lt;param-name&gt;debug&lt;/param-name&gt;<br />            &lt;param-value&gt;true&lt;/param-value&gt;<br />        &lt;/init-param&gt;<br />    &lt;/servlet&gt;<br /><br />    &lt;servlet-mapping&gt;<br />        &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br />        &lt;url-pattern&gt;/dwr/*&lt;/url-pattern&gt;<br />    &lt;/servlet-mapping&gt;</pre><br /><br />一
个可选的步骤是设置DWR为调试模式—象上面的例子那样—在servlet描述段中将debug参数设为true。当DWR在调试模式时，你可以从
HTMl网页中看到所有的可访问的Java对象。包含了可用对象列表的网页会出现在/WEBAPP/dwr这个url上，它显示了对象的公共方法。所列方
法可以从页面中调用，允许你，第一次，运行服务器上的对象的方法。下图显示了调试页的样子:<br /><br /><img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_07_000336_lrylCwRMxo.jpg" alt="" style="display: inline;" onmouseover="javascript:imgShowTip(this);" onload="javascript:imgLoad(this);" onclick="javascript:imgClick(this);" border="0" /><br />调试页<br /><br />现
在你必须让DWR知道通过XMLHttpRequest对象，什么对象将会接收请求。这个任务由叫做dwr.xml的配置文件来完成。在配置文件中，定义
了DWR允许你从网页中调用的对象。从设计上讲，DWR允许访问所有公布类的公共方法，但在我们的例子中，我们只允许访问几个方法。下面是我们示例的配置
文件:<br /><pre class="overflow">&lt;dwr&gt;<br />    &lt;allow&gt;<br />        &lt;convert converter="bean" match="dwr.sample.Apartment"/&gt;<br />        &lt;create creator="new" javascript="ApartmentDAO" class="dwr.sample.ApartmentDAO"&gt;<br />            &lt;include method="findApartments"/&gt;<br />            &lt;include method="countApartments"/&gt;<br />        &lt;/create&gt;<br />    &lt;/allow&gt;<br />&lt;/dwr&gt;</pre><br /><br />上
面的文件实现了我们例子中的两个目标。首先，&lt;convert&gt;标记告诉DWR将dwr.sample.Apartment对象的类型转换为
联合数组，因为，出于安全的原因，DWR默认的不会转换普通bean。第二，&lt;create&gt;标记让DWR暴露出
dwr.sample.ApartmentDAO类给JavaScript调用;我们在页面中使用JavaScript文件被javascript属性定
义。我们必须注意&lt;include&gt;标记，它指明了dwr.sample.ApartmentDAO类的哪些方法可用。<br /><br /><b><span style="font-size: 16px;">HTML/JSP代码</span></b><br />配
置完成后，你就可以启动你的Web应用了，这时DWR会为从你的HTML或Java服务器端页面(JSP)上调用所需方法作好准备，并不需要你创建
JavaScript文件。在search.jsp文件中，
我们必须增加由DWR提供的JavaScript接口，还有DWR引擎，加入以下三行到我们的代码中：<br /><br /><pre class="overflow">  &lt;script src='dwr/interface/ApartmentDAO.js'&gt;&lt;/script&gt;<br />  &lt;script src='dwr/engine.js'&gt;&lt;/script&gt;<br />  &lt;script src='dwr/util.js'&gt;&lt;/script&gt;</pre><br /><br />我
们注意到当用户改变搜索标准时，这是AJAX在示例程序中的首次应用;正如他所看到的，当标准改变时，可用的公寓数量被更新了。我创建了两个
JavaScript函数：当某一个选择下拉框中的值变化时被调用。ApartmentDAO.countApartments()函数是最重要的部分。
最有趣的是第一个参数,
loadTotal()函数，它指明了当接收到服务端的返回时DWR将会调用的JavaScript方法。loadTotal于是被调用来在HTML页面
的&lt;div&gt;中显示结果。下面是在这个交互场景中所使用到的JavaScript函数:<br /><br /><pre class="overflow">function updateTotal() {<br />    $("resultTable").style.display = 'none';<br />    var bedrooms = document.getElementById("bedrooms").value;<br />    var bathrooms = document.getElementById("bathrooms").value;<br />    var price = document.getElementById("price").value;<br />    ApartmentDAO.countApartments(loadTotal, bedrooms, bathrooms, price);<br />}<br /><br />function loadTotal(data) {<br />    document.getElementById("totalRecords").innerHTML = data;<br />}</pre><br /><br />很明显，用户想看到符合他的搜索条件的公寓列表。那么，当用户对他的搜索标准感到满意，并且总数也是有效的话，他会按下显示结果的按纽，这将会调用updateResults() JavaScript方法：<br /><br /><pre class="overflow">function updateResults() {<br />    <br />    DWRUtil.removeAllRows("apartmentsbody");<br />    var bedrooms = document.getElementById("bedrooms").value;<br />    var bathrooms = document.getElementById("bathrooms").value;<br />    var price = document.getElementById("price").value;<br />    ApartmentDAO.findApartments(fillTable, bedrooms, bathrooms, price);<br />    $("resultTable").style.display = '';<br />}<br /><br />function fillTable(apartment) {<br />    DWRUtil.addRows("apartmentsbody", apartment, [ getId, getAddress, getBedrooms, getBathrooms, getPrice ]);<br />}</pre><br /><br />updateResults
()方法清空了存放搜索返回结果的表域，从用户界面上获取所需参数，并且将这些参数传给DWR创建的ApartmentDAO对象。然后数据库查询将被执
行，fillTable()将会被调用，它解析了DWR返回的对象(apartment)，然后将其显示到页面中(apartmentsbody)。<br /><br /><b><span style="font-size: 16px;">安全因素</span></b><br />为
了保持示例的简要，ApartmentDAO类尽可能的保持简单，但这样的一个类通常有一组设置方法来操作数据，如insert(),
update()和delete()。DWR暴露了所有公共方法给所有的HTML页面调用。出于安全的原因，像这样暴露你的数据访问层是不明智的。开发者
可以创建一个门面来集中所有JavaScript函数与底层业务组件之间的通信，这样就限制了过多暴露的功能。<br /><br /><b><span style="font-size: 16px;">结论</span></b><br />这
篇文章仅仅让你在你的项目中使用由DWR支持的AJAX开了个头。DWR让你集中注意力在如何提高你的应用的交互模型上面，消除了编写和调试
JavaScript代码的负担。使用AJAX最有趣的挑战是定义在哪里和如何提高可用性。DWR负责了操作Web页面与你的Java对象之间的通信，这
样就帮助你完全集中注意力在如何让你的应用的用户界面更加友好，<br />我想感谢Mircea Oancea和Marcos Pereira,他们阅读了这篇文章并给予了非常有价值的返匮。<br /><br /><b>资源</b><br />·javaworld.com:<a href="http://www.javaworld.com/" target="_new">javaworld.com</a><br />·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn/</a><br />·onjava.com:<a href="http://www.onjava.com/" target="_new">onjava.com</a><br />·下载示例程序的全部源码:<a href="http://www.javaworld.com/javaworld/jw-06-2005/dwr/jw-0620-dwr.war" target="_new">http://www.javaworld.com/javaworld/jw-06-2005/dwr/jw-0620-dwr.war</a><br />·DWR: http://www.getahead.ltd.uk/dwr/index.html<br />·HSQL:http://hsqldb.sourceforge.net/<br />·AJAX的定义:http://en.wikipedia.org/wiki/AJAX<br />·
“AJAX:通向Web应用的新途径": Jesse James Garrett (Adaptive Path, 2005.2):
http://www.adaptivepath.com/publications/essays/archives/000385.php<br />· “非常动态的Web界面” Drew McLellan (xml.com, 2005.2): http://www.xml.com/pub/a/2005/02/09/xml-http-request.html<br />·XMLHttpRequest &amp; AJAX 工作范例: http://www.fiftyfoureleven.com/resources/programming/xmlhttprequest/examples<br />· “可用的XMLHttpRequest实践” Thomas Baekdal (Baekdal.com, 2005.3): http://www.baekdal.com/articles/Usability/usable-XMLHttpRequest/<br />·"XMLHttpRequest使用导引" Thomas Baekdal (Baekdal.com,  2005.2):http://www.baekdal.com/articles/Usability/XMLHttpRequest-guidelines/<br />·AJAX实质:http://www.ajaxmatters.com/<br /><img src ="http://www.blogjava.net/chenleiyu/aggbug/43028.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-04-25 15:08 <a href="http://www.blogjava.net/chenleiyu/archive/2006/04/25/43028.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)DWR初步</title><link>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43013.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 25 Apr 2006 06:23:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43013.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/43013.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/04/25/43013.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/43013.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/43013.html</trackback:ping><description><![CDATA[
		<span id="text131821" class="javascript">JAX(Asynchronous JavaScript
and XML,
非同步JavaScript與XML)可以說是2005年最受矚目的技術之一，雖然大多數人都認為這是個舊瓶裝新酒的整合技術，但是它的確解決許多以往撰
寫Web程式時，經常遇到的一些問題 ---------- 如何把Web寫得更AP。
<br /><br />很多以前寫AP可以輕鬆解決的功能，到了Stateless、Request Base…的Web環境，就綁手綁腳，甚至於無法達成。
<br /><br />AJAX的優勢很多文章都有討論，我就不再贅述，有興趣的人可以看看最有名的範例：
<br /><br />Google Map: <a class="ilink" target="_blank" href="http://maps.google.com/">http://maps.google.com/</a><br />Google Suggest: <a class="ilink" target="_blank" href="http://www.google.com/webhp?complete=1&amp;hl=en">http://www.google.com/webhp?complete=1&amp;hl=en</a><br /><br />但是實際開發時，雖然AJAX幫你解決一些以往的問題，它本身卻也帶來一些新的問題，例如是安全性、效率…，以及XML的使用不便。當你的系統，
所有功能都以AJAX方式開發時，你將會多了許多XML格式製作，以及資料mapping的問題，而且MS
DOM操作上又不是很方便，所以一些AJAX相關的Project應運而生，目前比較有名的有ASP.NET 2.0的Atlas、Midnight
Coders的WebORB...，以及本篇要談到的DWR。
<br /><br />DWR是一個Open Source Project，提供Java程式開發者一個前端(JavaScript)呼叫後端元件(例如Java
Bean、POJO、Spring的Bean…)的完整架構，在撰寫這些前後端程式時，可以完全不用理會AJAX的例行工作，也就是你不需要撰寫任何組成
XML、透過XMLHTTP傳遞資料、解析XML、把資料mapping到POJO…這些煩人的工作，下面我以一個最最簡單的範例，讓大家先看看它的優
點。
<br /><br />首先，你要先到下列網址下載DWR：
<br /><br /><a class="ilink" target="_blank" href="http://getahead.ltd.uk/dwr/download">http://getahead.ltd.uk/dwr/download</a><br /><br />建議您把Source File與WAR都下載，因為Source File裡面包含DWR會用到的相依Jar，當你在開發時，可以直接引用；而WAR則是DWR提供的範例，可自行安裝於Tomcat執行。
<br /><br /><center><font color="navy">一、建立inforce.dwr套件</font></center><br /><br />在套件裡建立First類別如下：
<br /><table class="java" bgcolor="#999999" border="0" cellpadding="3" cellspacing="1"><tbody><tr><td align="left" bgcolor="#dddddd" valign="top" width="1"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></font></pre></td><td align="left" bgcolor="#ffffff" valign="top"><pre><font class="java-reserved_word"><b>package</b></font> inforce.dwr;<br /> <br /><font class="java-reserved_word"><b>public</b></font><font class="java-reserved_word"><b>class</b></font> First <font class="java-bracket">{</font><br />  <font class="java-reserved_word"><b>public</b></font> String HelloWorld() <font class="java-bracket">{</font><br />    <font class="java-reserved_word"><b>return</b></font><font class="java-string">"歡迎體驗DWR!"</font>;<br />  <font class="java-bracket">}</font><br /><font class="java-bracket">}</font></pre></td></tr></tbody></table><br /><br />屬性HelloWorld()會固定回傳字串「歡迎體驗DWR!」。
<br /><br /><center><font color="navy">二、在web.xml加上下列描述：</font></center><br /><table class="java" bgcolor="#999999" border="0" cellpadding="3" cellspacing="1"><tbody><tr><td align="left" bgcolor="#dddddd" valign="top" width="1"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></font></pre></td><td align="left" bgcolor="#ffffff" valign="top"><pre>  &lt;servlet&gt;<br />    &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br />    &lt;display-name&gt;DWR Servlet&lt;/display-name&gt;<br />    &lt;description&gt;Direct Web Remoter Servlet&lt;/description&gt;<br />    &lt;servlet-class&gt;uk.ltd.getahead.dwr.DWRServlet&lt;/servlet-class&gt;<br />    &lt;init-param&gt;<br />      &lt;param-name&gt;debug&lt;/param-name&gt;<br />      &lt;param-value&gt;true&lt;/param-value&gt;<br />    &lt;/init-param&gt;<br />    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<br />  &lt;/servlet&gt;<br />  &lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/dwr<font class="java-comment">/*&lt;/url-pattern&gt;<br />  &lt;/servlet-mapping&gt;<br /></font></pre></td></tr></tbody></table><br /><br />所有/dwr/路徑下的網頁都會被導向uk.ltd.getahead.dwr.DWRServlet。
<br />在init-param裡，則指定開啟了除錯模式。
<br /><br /><center><font color="navy">三、在WEB-INF裡面，新增dwr.xml，內容如下：</font></center><br /><table class="java" bgcolor="#999999" border="0" cellpadding="3" cellspacing="1"><tbody><tr><td align="left" bgcolor="#dddddd" valign="top" width="1"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></font></pre></td><td align="left" bgcolor="#ffffff" valign="top"><pre>&lt;?xml version=<font class="java-string">"1.0"</font> encoding=<font class="java-string">"UTF-8"</font>?&gt;<br />&lt;!DOCTYPE dwr PUBLIC <font class="java-string">"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"</font><font class="java-string">"http://www.getahead.ltd.uk/dwr/dwr10.dtd"</font>&gt;<br /> <br />&lt;dwr&gt;<br />  &lt;allow&gt;<br />    &lt;create creator=<font class="java-string">"new"</font> javascript=<font class="java-string">"First"</font>&gt;<br />      &lt;param name=<font class="java-string">"class"</font> value=<font class="java-string">"inforce.dwr.First"</font> /&gt;<br />    &lt;/create&gt;<br />  &lt;/allow&gt;<br />&lt;/dwr&gt;<br /></pre></td></tr></tbody></table><br /><br />這裡用來宣告DWR可以使用的Java API，在這裡我們宣告了inforce.dwr.First，對應的JavaScript名稱是First。
<br /><br /><center><font color="navy">四、在WebContent新增「ch1.jsp」：</font></center><br /><table class="java" bgcolor="#999999" border="0" cellpadding="3" cellspacing="1"><tbody><tr><td align="left" bgcolor="#dddddd" valign="top" width="1"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></font></pre></td><td align="left" bgcolor="#ffffff" valign="top"><pre>&lt;%@ page language=<font class="java-string">"java"</font> contentType=<font class="java-string">"text/html; charset=utf-8"</font> pageEncoding=<font class="java-string">"utf-8"</font>%&gt;<br />&lt;!DOCTYPE html PUBLIC <font class="java-string">"-//W3C//DTD XHTML 1.0 Transitional//EN"</font><font class="java-string">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</font>&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />  &lt;title&gt;第一個DWR範例&lt;/title&gt;<br />  &lt;script type=<font class="java-string">'text/javascript'</font> src=<font class="java-string">'dwr/interface/First.js'</font>&gt;&lt;/script&gt;<br />  &lt;script type=<font class="java-string">'text/javascript'</font> src=<font class="java-string">'dwr/engine.js'</font>&gt;&lt;/script&gt;<br />  &lt;script type=<font class="java-string">'text/javascript'</font> src=<font class="java-string">'dwr/util.js'</font>&gt;&lt;/script&gt;<br />  &lt;script&gt;<br />    function callback(data) <font class="java-bracket">{</font><br />      alert(data);<br />    <font class="java-bracket">}</font><br />  &lt;/script&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;input type=<font class="java-string">'button'</font> value=<font class="java-string">'執行'</font> onclick=<font class="java-string">'First.HelloWorld(callback);'</font> /&gt; <br />&lt;/body&gt;<br />&lt;/html&gt;<br /></pre></td></tr></tbody></table><br /><br />除了在dwr.xml宣告外，在jsp裡面也要把類別對應的*.js引入進來，以First為例，如果你希望在jsp裡面用到First這個類別裡面的任何東西，都要宣告：
<br /><br /><table class="java" bgcolor="#999999" border="0" cellpadding="3" cellspacing="1"><tbody><tr><td align="left" bgcolor="#dddddd" valign="top" width="1"><pre><font color="#555555">1<br /></font></pre></td><td align="left" bgcolor="#ffffff" valign="top"><pre>  &lt;script type=<font class="java-string">'text/javascript'</font> src=<font class="java-string">'dwr/interface/First.js'</font>&gt;&lt;/script&gt;<br /></pre></td></tr></tbody></table><br /><br />其中，「dwr/」是你web專案的名稱。
<br />你不需要自己撰寫First.js這個檔案，DWR會自動處理，你只要有上面這行宣告就可以了。
<br /><br />網頁上只有一個按鈕，按下去會執行JavaScript：「First.HelloWorld(callback);」，First是我們剛剛在
dwr.xml設定的別名，也是就對應到inforce.dwr.First類別，所以這行JavaScript也就等於呼叫了
inforce.dwr.First.HelloWorld()。
<br /><br />「callback」是告訴DWR，呼叫First.HelloWorld()後要把它回傳的訊息丟到哪個前端JavaScript的函數。因此，這個按鈕按下去最後得到的結果，就是一個顯示「歡迎體驗DWR」的訊息框。
<br /><br />上面是DWR最簡單的使用方式，但是已經提供很多可能性，有興趣的人可以參考他們的範例：
<br /><br /><a class="ilink" target="_blank" href="http://getahead.ltd.uk/dwr/examples">http://getahead.ltd.uk/dwr/examples</a></span>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/43013.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-04-25 14:23 <a href="http://www.blogjava.net/chenleiyu/archive/2006/04/25/43013.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RoR历险记：一个Java程序员的Ruby on Rails体验</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37577.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Mar 2006 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37577.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/37577.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37577.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/37577.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/37577.html</trackback:ping><description><![CDATA[
		<p>ROR is Ruby on Rails. Ruby 众所周知已经是一种非常出色的动态语言 It's dynamic language。Simple and easy.动态语言是解释执行的，但性能上可能会打个折扣，但不是绝对的，因为应用是复杂的，性能是一种综合性的，这个在一个流传的测试中有回应。</p>
		<p>按照Rails4Days.pdf</p>
		<p>1.安装和配置出奇的简单。</p>
		<p>（真的很想提醒一下所有的java社区，充斥着各种框架的各个社区难道就没想到install吗？Everyone forgot to make install?　无论是J2EE，Spring,Hibernate,Struts等等，全部都没有 install。所有apache's jakarta的项目，竟然都没有install的，struts只有一个war，象一个demo，这种艰苦的环境会让linux fans笑倒的，ant和make都一样，怎么样也不能叫install。）</p>
		<p>安装完ruby182-14.exe，就用gem来安装rails。</p>
		<p>gem是ruby的install。gem install rails 就可以了。在回答问题的时候选y。</p>
		<p>2.要生成一个web应用更简单。<br />ruby rails &lt;目录名&gt;<br />一套web应用就生成了，配置一下数据库。config/database.yml，看一下就知道配置。<br />mysql要准备好，再创建数据表。</p>
		<p>3.代码生成<br />ruby script/generate &lt;类型&gt; &lt;名称&gt;<br />MVC都能生在，框架也可以，还有WebServices<br />http://rubyonrails.org/show/Generators<br />还有生成器可以下载，实在得令人心寒。</p>
		<p>当Hibernate3，EJB3还在那儿绕来绕去的时候，Spring,Hivemind还在那儿遮遮掩掩的时候，MDA和UML还在云山雾照的时候，ROR已经实干了。<br />可以代码生成，CRUD只要一句话，可以说演示非常方便，生成的代码量非常小，改起来很方便，使用一段时间就觉得很贴近业务思路，或者说人类语法。展示层还是象大多数嵌入式脚本，类JSP。但是由于是动态语言，非常精简，实际上看上去比标签精简多了。</p>
		<p>缺点：<br />大型应用是否支持？<br />有多少人支持？<br />性能？</p>
		<p>总的来说，人们对ROR的担心在于：能长久吗？程序是否稳定？各项性能？</p>
		<p>有一个性能测试，据说ROR&gt;Hibernate+Spring+Struts , 居然还多15-20%。敢这样说总有点来头的，况且开发速度要&gt;10倍以上。不过网址忘了，google上查一下就知道了。</p>
		<p>被各种框架折磨了很长时间，突然发现这么一个清新的东西，一直不习惯，怎么能这样呢，DAO总是要定义一个Base的，同一种find函数要写十个，现在怎么能这么简单呢，简单地都变成随意了。find_all就行了？find(@params[:id])就直接从参数里查询出对象？从 Tapestry还要OO，比Hibernate还要ORM，这简直是一种魔鬼。还能和C接口，关键业务放到C里去。比MVC还要MVC，目前为止明显的问题还是美工无法分离的问题，因为&lt;%for%&gt;这样的代码美工不可能来处理的。</p>
		<p>对象与数据库的连接更自然了，这让我想起了Delphi，由于只有Borland在控制，Object Pascal得以很优美，VCL很完整，ruby也是这样？</p>
		<p>看上去ruby吸收了非常多的java社区的思想，这也是一种有活力的表现，如果这种劲头保持下去，会象php一样来传染互联网。虽然php现在已经有老了的感觉，但由于一开始的清新空气使它一直生存到现在。</p>
		<p>ROR非常适合做应用，快速应用，这并不意味着不适合大型应用，目前因为还没看到大型应用，不敢乱说，心里也没底。底层应用不知道是否适合，因为一般来说底层应用往往是中级语言的世界，象C之类，Ruby应该也可以port进来，不过直接做底层可能性不大吧。</p>
		<p>想起了以前对Perl的形容，胶水，很贴切，本身好象做不了啥事，但可以迅速地粘起来一个东西来，如果胶水强度足够，甚至粘出一个大厦。Ruby也象是这样的胶水，现在在web方面比perl更强了。</p>
		<p>（刚打出了“恶业胃”三个字，猜一猜是什么意思？）</p>
		<p>最后说一句，fastcgi怎么也装不上，fastcgi for apache已经装好了，好象是fastcgi for ruby装不上。</p>
		<p>参见：</p>
		<p>ruby for apache:It's one-click install on windows and apache2 http://rubyforge.org/projects/rubyforapache</p>
		<p>fastcgi with ruby: http://wiki.rubyonrails.com/rails/show/FastCGI</p>
		<p>document for rails: http://documentation.rubyonrails.com/</p>
		<!-- 正文end -->
<img src ="http://www.blogjava.net/chenleiyu/aggbug/37577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-27 11:56 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/27/37577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ruby on rails</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37575.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Mar 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37575.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/37575.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37575.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/37575.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/37575.html</trackback:ping><description><![CDATA[
		<img src="http://www.douban.com/lpic/s1488229.jpg" />
<img src ="http://www.blogjava.net/chenleiyu/aggbug/37575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-27 11:45 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/27/37575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最轻省的MVC,ORM方案原来在别处--Ruby on Rails 2nd </title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37573.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Mar 2006 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37573.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/37573.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37573.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/37573.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/37573.html</trackback:ping><description><![CDATA[
		<p>2nd  2005.7.15 加入与J2EE内框架 的对比。</p>
		<p>  Java世界里几百个ORM Framework和MVC Framework妖精打架，吸引了全世界大部分目光的时候，<a href="http://www.rubyonrails.org/">Ruby on Rails</a>悄悄出现了。<br /><br /><strong>优点<br /></strong>    1.简洁。因为是Pragmatic Programer那帮子人搞出来的东西，所以非常Pragmatic，Ruby本身的动态语言优势加上强悍的、高度自动化、疯狂使用了反射的基类，使得Rails的代码量只有Struts/Webwork+Spring+Hibernate的N份之一，默认的CRUD系统，只需要一行代码。</p>
		<p>    2.性能。据<a href="http://developers.slashdot.org/article.pl?sid=05/04/04/1520227&amp;from=rss">评测</a>，速度居然还比Struts+Spring+Hibernate的速度快15%-30%。</p>
		<p>    3.良好的语义和领域驱动开发。<br />        我觉得这点是最重要的，<a href="http://ar.rubyonrails.com/files/README.html">Active Record</a> 采用了与Hibernate的Data Mapper不同的另一种领域模式--Active Record，获得了最直接的DDD模式。而<a href="http://ap.rubyonrails.com/files/README.html">Action Pack</a> 也吸取了众多MVC框架的优点，同时去掉了他们的复杂性而成为亮点。还有Ruby能把语义定义得像DSL一样。让编程语言口语化也是我所关注的。<br /> <br /><strong>vs J2EE (first round)</strong></p>
		<p>     因为以上优点，说它悄悄出现其实不全对，已经有不少人开始不安的拿Rails与java的方案作对比，结果不外乎，Rails在中小型项目表现很好，但还不能适应复杂的商业逻辑。但是，就像EJB说别人不支持分布式一样，是不是真有这么多复杂的模型和逻辑呢，而且，Rails还在发展，以后如何还未知。</p>
		<p>
				<strong>学习目的</strong>
		</p>
		<p>        一来可以在中小项目里直接应用Rails从而获得N倍的开发速度，<br />　　二来更重要的是反过来影响在Java World里面ORM,、MVC的设计思路，改善自己的框架。<br />　　而且，Rails的入门有个特点，花上１５分钟，写一行代码，就能实现第一个完整的CRUD系统，不像别的语言只能打印一句Hello world，真正useful的话要搞半天。</p>
		<p>
				<strong>架构</strong>    <br />    ROR的架构图如下，服务器是Apache，根基是我认为最简洁的MVC方案--<a href="http://ap.rubyonrails.com/files/README.html">Action Pack</a>和最DDD的ORM方案--<a href="http://ar.rubyonrails.com/files/README.html">Active Record</a>，再加上类似JSP的view template和小小的代码生成器，就是Rails了。<br />    <img alt="" src="http://www.rubyonrails.org/images/request_cycle.png" /></p>
		<p>
				<strong>vs J2EE (second round)</strong>
		</p>
		<p>
				<span class="atitle">IBM DW上的文章 <a href="http://www-128.ibm.com/developerworks/java/library/wa-rubyonrails/index.html?ca=drs-">Ruby on Rails and J2EE: Is there room for both?</a> 有详细的对比，推荐</span>
		</p>
		<p>
				<span class="atitle">
						<img alt="" src="http://www-128.ibm.com/developerworks/java/library/wa-rubyonrails/stack.jpg" />
				</span>
		</p>
		<p>
				<strong>
				</strong>
		</p>
		<p>可见，两者都是基于MVC与ORM模式，但两者的实现哲学不同。<br />MVC层，两者都是基于FrontController模式，通过DispatchServlet/ActionServlet ，把request映射到恰当的controller。不过Struts需要用xml详细配置每一个映射和Forward，而Rails则按照默认原则进行配置。<br />另外，Struts提倡每个ActionServlet完成一个动作，而Rails提倡把一组相关的动作放在一个Controller中。</p>
		<p>ORM层，Data Mapper与Active Recorder模式的区别相当大。<br />Hibernate的Data Mapper模式，需要由Session充当Mapper 对POJO在DB之间进行映射。<br /><br />Active Recorder模式则是让POJO封装DB中的一行数据，同时拥有CRUD的默认操作，针对一组POJO的操作如find, delete，则放在类的静态方法中。因为没有了作为Mapper的Session，整个程序很自然，很DDD。</p>
		<p>
				<strong>学习资料</strong>
		</p>
		<p> 0. 隆重推荐<br />    koalant的<a href="http://www.koalant.com/rubyonrails.pdf">Ruby on Rails实践</a>  (中文版，文笔清晰，看完它基本不用再看其他教程了)</p>
		<p>1. 快速指南<br />       　<a href="http://www.onlamp.com/lpt/a/5546">Rolling with Ruby on Rails part1</a> ，<u><font color="#0000ff">part2</font></u><br />       　<a id="_1bcd16c157b065b9_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl2_TitleUrl" href="http://blog.csdn.net/ruby_cn/archive/2004/11/04/167274.aspx">用Rails创建web应用</a> <br /><br />2. Ruby资料<br />　　《Programing Ruby 2nd》Progmatic bookshelf成员，第1版是免费的，随Ruby安装包附送。<br />           而包含了最新Ruby1.8的第２版要收费，好在Emule上可以下到电子版。<br />　　<a href="http://www.ruby-cn.org/book/ProgrammingRuby/index.html">《Progarming Ruby 1st》的部分中文翻译</a></p>
		<p>3. Active Record和Action Pack的资料<br />　　<a href="http://ar.rubyonrails.com/files/README.html">Active Record</a><br />　　<a href="http://ap.rubyonrails.com/files/README.html">Action Pack</a></p>
		<p>4.Rails作者及其他Rubyer的blog<br />　　<a href="http://www.loudthinking.com/">David H. Heinemeier--loud thinking</a>　Rails的领导者<br />　　<a href="http://www.toolshed.com/blog">Andy</a>　<br />　　<a href="http://pragprog.com/pragdave">Dave Thomas</a><br /><br />5.Ruby站点<br />　   <a href="http://www.rubyonrails.org/">Ruby on Rails</a><br />       <a href="http://rubyforge.org/">RubyForge </a>       Ruby的SourceForge<br />       <a href="http://www.rubygarden.org/">RubyGarden</a><br />       <a href="http://www.rubychina.org/">Ruby中国</a><br />       <a href="http://www.ruby-cn.org/">Ruby-CN</a></p>
<img src ="http://www.blogjava.net/chenleiyu/aggbug/37573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-27 11:30 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/27/37573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Velocity用户手册</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37570.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Mar 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37570.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/37570.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/27/37570.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/37570.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/37570.html</trackback:ping><description><![CDATA[Velocity是什么？<br />Velocity是一个基于java的模板引擎（template engine）。它允许任何人仅仅简单的使用模板语言（template language）来引用由java代码定义的对象。<br />当Velocity应用于web开发时，界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点，也就是说，页面设计人员可以只关注页面的显示效果，而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来，这样为web站点的长期维护提供了便利，同时也为我们在JSP和PHP之外又提供了一种可选的方案。<br />Velocity的能力远不止web站点开发这个领域，例如，它可以从模板（template）产生SQL和PostScript、XML，它也可以被当作一个独立工具来产生源代码和报告，或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务（template service）。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。<br /><br />Velocity能为我们作什么？<br />The Mud Store Example <br />假设你是一家专门出售Mud的在线商店的页面设计人员，让我们暂且称它为“在线MUD商店”。你们的业务很旺，客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站，登陆后就允许他们查看订单并购买更多的mud。现在，一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud，由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的，所以某天有一个问题可能会冒出来：为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢？<br />Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员，你希望每个用户登陆时都拥有自己的页面。<br />你会见了一些公司内的软件工程师，你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边，看一看你应该作些什么吧。<br />你可能在页面内嵌套如下的VTL声明：<br />&lt;html&gt;<br />&lt;body&gt;<br />Hello $customer.Name!<br />&lt;table&gt;<br />#foreach( $mud in $nudsOnSpecial ) <br />#if ( $customer.hasPurchased( $mud ) ) <br />&lt;tr&gt;&lt;td&gt;$flogger.getPromo( $mud )&lt;/td&gt;&lt;/tr&gt;<br />#end<br />#end<br />&lt;/table&gt;<br /><br />Velocity Template Language(VTL):AN introduction<br />VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。<br />VTL使用references来在web site内嵌套动态内容，一个变量就是一种类型的reference。变量是某种类型的refreence，它可以指向java代码中的定义，或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子，它可以被嵌套到HTML代码中：<br />  #set ( $a = “Velocity” )<br />和所有的VTL statement一样，这个statement以＃字符开始并且包含一个directive：set。当一个在线用户请求你的页面时，Velocity Templating Engine将查询整个页面以便发现所有＃字符，然后确定哪些是VTL statement，哪些不需要VTL作任何事情。<br />＃字符后紧跟一个directive：set时，这个set directive使用一个表达式（使用括号封闭）――一个方程式分配一个值给变量。变量被列在左边，而它的值被列在右边，最后他们之间使用＝号分割。<br />在上面的例子中，变量是$a，而它的值是Velocity。和其他的references一样以$字符开始，而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。<br />记住以下的规则：<br />使用$字符开始的references用于得到什么；使用#字符开始的directives用于作些什么。<br />Hello Velocity World!<br />一旦某个变量被分配了一个值，那么你就可以在HTML文件的任何地方引用它。在下面的例子中，一个值被分配给$foo变量，并在其后被引用。<br />&lt;html&gt;<br />&lt;body&gt;<br />#set ( $foo = “Velocity” )<br />Hello $foo World!<br />&lt;/body&gt;<br />&lt;/html&gt;<br />上面的实现结果是在页面上打印“Hello Velocity World！”<br />为了使包含VTL directives的statement更具有可读性，我们鼓励你在新行开始每个VTL statement，尽管你不是必须这么作。Set directive将在后面详细描述。<br />注释<br />单行注释：<br />  ## This is a single line comment.<br />多行注释：<br />  #*<br />   Thus begins a multi-line comment. Online visitors won’t<br />   see this text because the Velocity Templating Engine will<br />ignore it.<br />  *#<br />文档格式：<br />  #**<br />   This is a VTL comment block and<br />   may be used to store such information<br />as the document author and versioning<br />   information:<br />   @version 5<br />   @author<br />  *#<br /><br />References<br />在VTL中有三种类型的references：变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者，你和你的工程师必须就references的名称达成共识，以便你可以在你的template中使用它们。<br />Everything coming to and from a reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象，那么Velocity将调用它的toString()方法将这个对象转型为String类型。<br />  变量 <br />  格式要求同java。<br />  属性 <br />  例子：<br />    $customer.Address<br />    $purchase.Total<br />$customer.Address有两种含义。它可以表示：查找hashtable对象customer中以Address为关键字的值；也可以表示调用customer对象的getAddress()方法。当你的页面被请求时，Velocity将确定以上两种方式选用那种，然后返回适当的值。<br />方法 <br />一个方法就是被定义在java中的一段代码，并且它有完成某些有用工作的能力，例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References，一般还包括一个VTL方法体。例如：<br />  $customer.getAddress()<br />  $purchase.getTotal()<br />  $page.setTitle( “My Home Page” )<br />  $person.setAttributes( [“Strange”, “Weird”, “Excited”] )<br />前两个例子$customer.getAddress()和$purchase.getTotal()看起来挺想上面的属性$customer.Address 和 $purchase.Total。如果你觉得他们之间有某种联系的话，那你是正确的。<br />VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。<br />正式reference标记<br />reference的正是格式如下：<br />  ${mudSlinger}        变量<br />  ${customer.Address}    属性<br />  ${purchase.getTotal()}    方法<br />非正是格式更见常用，但是有时还是使用正是格式比较适合。例如：你希望通过一个变量$vice来动态的组织一个字符串。<br />  Jack is a $vicemaniac.<br />本来变量是$vice现在却变成了$vicemaniac，这样Veloctiy就不知道您到底要什么了。所以，应该使用正是格式书写<br />  Jack is a ${vice}maniac<br />现在Velocity知道变量是$vice而不是$vicemaniac。<br />Quiet reference notation<br />例如：<br />  &lt;input type=”text” name=”email” value=”$email” /&gt;<br />当页面的form被初始加载时，变量$email还没有值，这时你肯定是希望它能够显示一个blank text来代替输出”$email”这样的字段。那么使用quiet reference notation就比较合适。<br />  &lt;input type=”text” name=”email” value=”$!email”/&gt;<br />这样文本框的初始值就不会是email而是空值了。<br />正式和quiet格式的reference notation也可一同使用，像下面这样：<br />  &lt;input type=”text” name=”email” value=”$!{email}”/&gt;<br />Getting literal <br />Velocity使用特殊字符$和#来帮助它工作，所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。<br />  货币字符 <br />在VTL中使用$2.5这样的货币标识是没有问题得的，VTL不会将它错认为是一个reference，因为VTL中的reference总是以一个大写或者小写的字母开始。<br />Escaping valid VTL reference <br />VTL中使用“\”作为逃逸符。<br />例如：<br />  #set( $email = “foo” )<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />将render为：<br />  foo<br />  $email<br />  \foo<br />  \\$email<br />如果email变量没有被定义则<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />将被render为：<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />注意：VTL中未被定义的变量将被认为是一个字符串，所以以下例子：<br />  #set( $foo = “gibbous” )<br />  $moon = $foo<br />的输出结果是：<br />$moon = gibbous<br />Case substitution<br />现在你已经对reference比较熟悉了，你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如：<br />  $foo<br />  $foo.getBar()<br />  ## is the same as<br />  $foo.Bar<br /><br />  $data.getUser(“jon”)<br />  ## is the same as<br />  $data.User(“jon”)<br /><br />  $data.getRequest().getServerName()<br />  # is the same as<br />  $data.Request.ServerName<br />  ## is the same as<br />  ${data.Request.ServerName}<br />但是，注意VTL中不会将reference解释为对象的实例变量。例如：$foo.Name将被解释为Foo对象的getName（）方法，而不是Foo对象的Name实例变量。<br />Directives<br />Reference允许设计者使用动态的内容，而directive使得你可以应用java代码来控制你的显示逻辑，从而达到你所期望的显示效果。<br />  #set <br />  #set directive被用于设置一个reference的值。例如：<br />    #set ( $primate = “monkey” )<br />    #set ( $customer.Behavior = $primate )<br />赋值左侧的（LHS）必须是一个变量或者属性reference。右侧（RHS）可以是以下类型中一种：<br />l  变量reference<br />l  String literal<br />l  属性reference<br />l  方法reference<br />l  number literal<br />l  ArrayList<br />下面是应用各种类型的RHS的例子：<br />  ＃set ( $monkey = $bill ) ##变量reference<br />  ＃set ( $monkey.Friend = “monica” ) ##String literal<br />  ＃set ( $monkey.Blame = $whitehouse.Leak )##属性reference<br />  ＃set ( $monkey.Plan = $spindoctor.weave($web) )##方法reference<br />  ＃set ( $monkey.Number = 123 )##Number literal<br />  ＃set ( $monkey.Say = [“Not”, $my, “fault”] )##ArrayList<br />注意：最后一个例子的取值方法为：$monkey.Say.get(0)<br />RHS也可以是一个简单的算术表达式：<br />  #set ( $value = $foo + 1 )<br />  #set ( $value = $bar -1 )<br />#set ( $value = $foo * $bar )<br />#set ( $value = $foo / $bar )<br />如果你的RHS是一个null，VTL的处理将比较特殊：它将指向一个已经存在的reference，这对初学者来讲可能是比较费解的。例如：<br />  #set ( $resut = $query.criteria(“name”) )<br />  The result of the first query is $result<br /><br />  #set ( $resut = $query.criteria(“address”) )<br />  The result of the second query is $result<br />如果$query.criteria(“name”)返回一个“bill”，而$query.criteria(“address”)返回的是null，则显示的结果如下：<br />  The result of the first query is bill<br />  The result of the first query is bill<br />看看下面的例子：<br />  #set( $criteria = ["name", "address"] )<br />#foreach( $criterion in $criteria )<br />#set( $result = $query.criteria($criterion) )<br />  #if( $result )<br />  Query was successful<br />     #end<br />#end<br />在上面的例子中，程序将不能智能的根据$result的值决定查询是否成功。在$result被#set后（added to the context），它不能被设置回null（removed from the context）。打印的结果将显示两次查询结果都成功了，但是实际上有一个查询是失败的。<br />为了解决以上问题我们可以通过预先定义的方式：<br />  #set( $criteria = [“name”, “address”] )<br />  #foreach( $criterion in $criteria )<br />    #set( $result = false )<br />    #set( $result = $query.criteria( $criterion ) )<br />    #if( $result )<br />      Query was successful<br />    #end<br />  #end<br />  String Literals <br />  当你使用#set directive，String literal封闭在一对双引号内。<br />    #set ( $directoryRoot = “www” )<br />    #set ( $templateName = “index.vm” )<br />    #set ( $template = “$directoryRoot/$tempateName” )<br />    $template<br />  上面这段代码的输出结果为：www/index.vm<br />  但是，当string literal被封装在单引号内时，它将不被解析：<br />    #set ( $foo = “bar” )<br />    $foo<br />    #set ( $blargh = ‘$foo’ )<br />  结果：<br />    bar<br />    $foo<br />  上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。<br />条件语句<br />  if/elseif/else <br />当一个web页面被生成时使用Velocity的#if directrive，如果条件成立的话可以在页面内嵌入文字。例如：<br />  #if ( $foo )<br />    &lt;strong&gt;Velocity!&lt;/strong&gt;<br />  #end<br />上例中的条件语句将在以下两种条件下成立：<br />l  $foo是一个boolean型的变量，且它的值为true<br />l  $foo变量的值不为null<br />这里需要注意一点：Velocity context仅仅能够包含对象，所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值，Velocity也会利用内省机制将它转换为一个Boolean的相同值。<br />如果条件成立，那么#if和#end之间的内容将被显示。<br />#elseif和#else元素可以同#if一同使用。例如：<br />  #if( $foo &lt; 10 )<br />    &lt;strong&gt; Go North &lt;/strong&gt;<br />  #elseif( $foo == 10 )<br />    &lt;strong&gt; Go East &lt;/strong&gt;<br />  #elseif( $foo == 6 )<br />    &lt;strong&gt; Go South &lt;/strong&gt;<br />  #else<br />    &lt;strong&gt; Go West &lt;/strong&gt;<br />  #end<br />注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false，但是与java不同它使用“＝＝”来比较两个值，而且velocity要求等号两边的值类型相同。<br />关系、逻辑运算符 <br />Velocity中使用等号操作符判断两个变量的关系。例如：<br />#set ( $foo = “deoxyribonucleic acid” )<br />#set ( $bar = “ribonucleic acid” )<br />#if ( $foo == $foo )<br />  In this case it’s clear they aren’t equivalent.So…<br />#else<br />  They are not equivalent and this will be the output.<br />#end<br /><br />Velocity有AND、OR和NOT逻辑运算符。下面是一些例子：<br />  ## logical AND<br />  #if( $foo &amp;&amp; $bar )<br />    &lt;strong&gt; This AND that &lt;/strong&gt;<br />  #end<br /><br />  ## logical OR<br />  #if ( $foo || $bar )<br />    &lt;strong&gt;This OR That &lt;/strong&gt;<br />  #end<br /><br />  ##logical NOT<br />  #if ( !$foo )<br />    &lt;strong&gt; NOT that &lt;/strong&gt;<br />  #end<br />循环 <br />  Foreach循环 <br />  例子：<br />    &lt;ul&gt;<br />      #foreach ( $product in $allProducts )<br />        &lt;li&gt; $product &lt;/li&gt;<br />      #end<br />    &lt;/ul&gt;<br />  每次循环$allProducts中的一个值都会赋给$product变量。<br />$allProducts可以是一个Vector、Hashtable或者Array。分配给$product的值是一个java对象，并且可以通过变量被引用。例如：如果$product是一个java的Product类，并且这个产品的名字可以通过调用他的getName（）方法得到。<br />现在我们假设$allProducts是一个Hashtable，如果你希望得到它的key应该像下面这样：<br />&lt;ul&gt;<br />#foreach ( $key in $allProducts.keySet() )<br />&lt;li&gt;Key: $key -&gt; Value: $allProducts.get($key) &lt;/li&gt;<br />#end<br />&lt;/ul&gt;<br /><br />Velocity还特别提供了得到循环次数的方法，以便你可以像下面这样作：<br />&lt;table&gt;<br />#foreach ( $customer in $customerList )<br />&lt;tr&gt;&lt;td&gt;$velocityCount&lt;/td&gt;&lt;td&gt;$customer.Name&lt;/td&gt;&lt;/tr&gt;<br />#end<br />&lt;/table&gt;<br />$velocityCount变量的名字是Velocity默认的名字，你也可以通过修改velocity.properties文件来改变它。默认情况下，计数从“1”开始，但是你可以在velocity.properties设置它是从“1”还是从“0”开始。下面就是文件中的配置：<br />  # Default name of loop counter<br />  # variable reference<br />  directive.foreach.counter.name = velocityCount<br /><br />  # Default starting value of the loop<br />  # counter variable reference<br />  directive.foreach.counter.initial.value = 1<br /><br />include<br />#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因，被引入的本地文件只能在TEMPLATE_ROOT目录下。<br />  #inclued ( “one.txt” )<br />如果您需要引入多个文件，可以用逗号分隔就行：<br />  #include ( “one.gif”, “two.txt”, “three.htm” )<br />在括号内可以是文件名，但是更多的时候是使用变量的：<br />  #inclue ( “greetings.txt”, $seasonalstock )<br /><br />parse<br />#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTL并render模板。<br />  #parse( “me.vm” )<br />就像#include，#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是，#parse只能指定单个对象。<br />你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10。#parse是可以递归调用的，例如：如果dofoo.vm包含如下行：<br />  Count down.<br />  #set ( $count = 8 )<br />  #parse ( “parsefoo.vm” )<br />  All done with dofoo.vm!<br />那么在parsefoo.vm模板中，你可以包含如下VTL：<br />  $count<br />  #set ( $count = $count – 1 )<br />  #if ( $count &gt; 0 )<br />    #parse( “parsefoo.vm” )<br />  #else<br />    All done with parsefoo.vm!<br />  #end<br />的显示结果为：<br />  Count down.<br />  8<br />  7<br />  6<br />  5<br />  4<br />  3<br />  2<br />  1<br />  0<br />  All done with parsefoo.vm!<br />All done with dofoo.vm!<br /><br />Stop<br />#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于debug是很有帮助的。<br />  #stop<br /><br />Velocimacros<br />#macro script element允许模板设计者定义一段可重用的VTL template。例如：<br />  #macro ( d )<br />  &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;<br />  #end<br />在上面的例子中Velocimacro被定义为d，然后你就可以在任何VTL directive中以如下方式调用它：<br />  #d()<br />当你的template被调用时，Velocity将用&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;替换为#d()。<br />每个Velocimacro可以拥有任意数量的参数――甚至0个参数，虽然定义时可以随意设置参数数量，但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro，一个参数是color另一个参数是array：<br />  #macro ( tablerows $color $somelist )<br />  #foreach ( $something in $somelist )<br />    &lt;tr&gt;&lt;td bgcolor=$color&gt;$something&lt;/td&lt;/tr&gt;<br />  #end<br />  #end<br />调用#tablerows Velocimacro：<br />  #set ( $greatlakes = [ “Superior”, “Michigan”, “Huron”, “Erie”, “Ontario” ] )<br />  #set ( $color = “blue” )<br />  &lt;table&gt;<br />    #tablerows( $color $greatlakes )<br />  &lt;/table&gt;<br />经过以上的调用将产生如下的显示结果：<br />  &lt;table&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Superior &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Michigan &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Huron &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Erie &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Ontario &lt;/td&gt;&lt;/tr&gt;<br />  &lt;/table&gt;<br />Velocimacros可以在Velocity模板内实现行内定义（inline），也就意味着同一个web site内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的：它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。<br />上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(在velocity.properties中定义)里，所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用：<br />  #set ( $parts = [ “volva”, “stipe”, “annulus”, “gills”, “pileus” ] )<br />  #set ( $cellbgcol = “#CC00FF” )<br />  &lt;table&gt;<br />    #tablerows( $cellbgcol $parts )<br />  &lt;/table&gt;<br />上面VTL将产生如下的输出：<br />  &lt;table&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; volva &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; stipe &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; annulus &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; gills &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; pileus &lt;/td&lt;/tr&gt;<br />  &lt;/table&gt;<br />  Velocimacro arguments <br />  Velocimacro可以使用以下任何元素作为参数：<br />l  Reference：任何以$开头的reference<br />l  String literal：<br />l  Number literal：<br />l  IntegerRange：[1….3]或者[$foo….$bar]<br />l  对象数组：[“a”,”b”,”c”]<br />l  boolean值：true、false<br />当将一个reference作为参数传递给Velocimacro时，请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro，而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如：<br />  #macro ( callme $a )<br />    $a $a $a<br />  #end<br />  #callme( $foo.bar() )<br />执行的结果是：reference $foo的bar（）方法被执行了三次。<br />如果你不需要这样的特性可以通过以下方法：<br />  #set ( $myval = $foo.bar() )<br />  #callme ( $myval )<br /><br />Velocimacro properties <br />Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide。<br />Velocity.properties文件中的velocimacro.libraary：一个以逗号分隔的模板库列表。默认情况下，velocity查找唯一的一个库：VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。<br />Velocity.properties文件中的velocimacro.permissions.allow.inline属性：有两个可选的值true或者false，通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros。<br />Velocity.properties文件中的<br />velocimacro.permissions.allow.inline.replace.global属性有两个可选值true和false，这个属性允许使用者确定inline的Velocimacro定义是否可以替代全局Velocimacro定义（比如在velocimacro.library属性中指定的文件内定义的Velocimacro）。默认情况下，此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。<br />Velocity.properties文件中的<br />velocimacro.permissions.allow.inline.local.scale属性也是有true和false两个可选值，默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说，如果这个属性设置为true，一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM敲门：a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected。<br />Velocity.properties文件中的velocimacro.context.localscope属性有true和false两个可选值，默认值为false。当设置为true时，任何在Velocimacro内通过#set()对context的修改被认为是针对此velocimacro的本地设置，而不会永久的影响内容。<br />Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时，对于一个Velocimacro的调用将自动检查原始库是否发生了变化，如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果，就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态（file.resource.loader.cache = false）。注意这个属性实际上是针对开发而非产品的。<br />Velocimacro Trivia <br />Velocimacro必须被定义在他们被使用之前。也就是说，你的#macro()声明应该出现在使用Velocimacros之前。<br />特别要注意的是，如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。<br />Escaping VTL directives<br />VTL directives can be escaped with “\”号，使用方式跟VTL的reference使用逃逸符的格式差不多。<br />  ## #include( “a.txt” ) renders as &lt;ontents of a.txt&gt;(注释行)<br />  #include( “a.txt” )<br /><br />  ## \#include( “a.txt” ) renders as \#include( “a.txt” )<br />  \#include( “a.txt” )<br /><br />  ## \\#include ( “a.txt” ) renders as \&lt;contents of a.txt&gt;<br />  \\#include( “a.txt” )<br />在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心（比如在一个if-else-end statement内）。下面是VTL的if-statement的典型应用：<br />  #if ( $jazz )<br />    Vyacheslav Ganelin<br />  #end<br />如果$jazz是ture，输出将是：<br />  Vyacheslav Ganelin<br />如果$jazz是false，将没有输出。使用逃逸符将改变输出。考虑一下下面的情况：<br />  \#if ( $jazz )<br />    Vyacheslav Ganelin<br />  \#end<br />现在无论$jazz是true还是false，输出结果都是：<br />  #if ( $jazz )<br />    Vyacheslav Ganelin<br />  #end<br />事实上，由于你使用了逃逸符，$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的，例如：<br />  \\#if ( $jazz )<br />    Vyacheslav Ganelin<br />  \\#end<br />以上程序的显示结果为：<br />  \ Vyacheslav Ganelin<br />  \<br />但是如果$jazz为false，那么将没有输出。（书上说会没有输出，但是我觉得应该还有有“\”字符被输出。）<br />VTL：Formatting issues<br />尽管在此用户手册中VTL通常都开始一个新行，如下所示：<br />  #set ( $imperial = [ “Munetaka”, “Koreyasu”, “Hisakira”, “Morikune” ] )<br />  #foreach ( $shogun in $imperial )<br />    $shogun<br />  #end<br />但是像下面这种写法也是可以的：<br />  Send me #set($foo = [“$10 and”,”a cake”])#foreach($a in $foo)$a #end please.<br />上面的代码可以被改写为：<br />  Send me<br />  #set ( $foo = [“$10 and “,”a cake”] )<br />  #foreach ( $a in $foo )<br />    $a<br />  #end<br />  please.<br />或者<br />  Send me<br />  #set($foo = [“$10 and “,”a cake”])<br />        #foreach （$a in $foo ）$a<br />      #end please.<br />这两种的输出结构将一样。<br />其他特性和杂项<br />  math   在模板中可以使用Velocity内建的算术函数，如：加、减、乘、除<br />    #set ( $foo = $bar + 3 )<br />    #set ( $foo = $bar - 4 )<br />    #set ( $foo = $bar * 6 )<br />    #set ( $foo = $bar / 2 )<br />  当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到：<br />    #set ( $foo = $bar % 5 )<br />在Velocity内使用数学计算公式时，只能使用像-n,-2,-1,0,1,2,n这样的整数，而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。<br />Range Operator<br />Range operator可以被用于与#set和#foreach statement联合使用。对于处理一个整型数组它是很有用的，Range operator具有以下构造形式：<br />  [n..m]<br />m和n都必须是整型，而m是否大于n则无关紧要。例子：<br />  First example:<br />  #foreach ( $foo in [1..5] )<br />    $foo<br />  #end<br /><br />  Second example:<br />  #foreach ( $bar in [2..-2] )<br />    $bar<br />  #end<br /><br />  Third example:<br />  #set ( $arr = [0..1] )<br />  #foreach ( $i in $arr )<br />    $i<br />  #end<br /><br />  Fourth example:<br />  [1..3]<br />上面四个例子的输出结果为：<br />  First example：<br />  1 2 3 4 5<br /><br />  Second example：<br />  2 1 0 -1 -2<br /><br />  Third example：<br />  0 1<br /><br />  Fourth example：<br />  [1..3]<br />注意：range operator只在#set和#foreach中有效。<br />Advanced Issue：Escaping and！<br />当一个reference被“！”分隔时，并且在它之前有逃逸符时，reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下： <br />#set ( $foo = “bar” )<br />特殊形式  标准格式<br />Render前  Render后  Render前  Render后<br />$\!foo  $!foo  \$foo  \$foo<br />$\!{foo}  $!{foo}  \$!foo  \$!foo<br />$\\!foo  $\!foo  \$!{foo}  \$!{foo}<br />$\\\!foo  $\\!foo  \\$!{foo}  \bar<br />Velocimacro杂记<br />  Can I user a directive or another VM as an argument to a VM? <br />  例如：#center ( #bold( “hello” ) )<br />  不可以。一个directive的参数使用另外一个directive是不合法的。<br />  但是，还是有些事情你可以作的。最简单的方式就是使用双引号：<br />    #set ( $stuff = “#bold( ‘hello’ )” )<br />    #center ( $stuff ) <br />  上面的格式也可以缩写为一行：<br />    #center ( “#bold( ‘hello’ ) )<br />请注意在下面的例子中参数被evaluated在Velocimacro内部，而不是在calling level。例子：<br />  #macro ( inner $foo )<br />    inner : $foo<br />  #end<br /><br />  #macro ( outer $foo )<br />    #set ( $bar = “outerlala” )<br />    outer : $foo<br />  #end<br />  <br />  #set ( $bar = ‘calltimelala’ )<br />  #outer( “#inner($bar)” )<br />输出结果为：<br />  outer : inner : outerlala<br />记住Veloctiy的特性：参数的传递是By Name的。例如：<br />  #macro ( foo $color )<br />    &lt;tr bgcolor = $color &gt;&lt;td&gt;Hi&lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr bgcolor = $color &gt;&lt;td&gt;There&lt;/td&gt;&lt;/tr&gt;<br />  #end<br /><br />  #foo ( $bar.rowColor() )<br />以上代码将导致rowColor()方法两次调用，而不是一次。为了避免这种现象的出现，我们可以按照下面的方式执行：<br />  #set ( $color = $bar.rowColor() )<br />  #foo ( $color )<br />can I register velocimacros via #parse()? <br />目前，Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。<br />如果你试图#parse()一个包含#macro() directive的模板，这一点是需要牢记的。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。<br />What is velocimacro autoreloading？ <br />velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false。<br />String concatenation<br />开发人员最常问的问题是我如何作字符拼接？在java中是使用“＋”号来完成的。<br />在VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如：<br />#set ( $size = “Big” )<br />#set ( $name = “Ben” )<br />The clock is $size$name.<br />输出结果将是：The clock is BigBen.。更有趣的情况是：<br />  #set ( $size = “Big” )<br />  #set ( $name = “Ben” )<br />  #set ( $clokc = “$size$name” )<br />  The clock is $clock.<br />上例也会得到同样的结果。最后一个例子，当你希望混合固定字段到你的reference时，你需要使用标准格式：<br />  #set ( $size = “Big” )<br />  #set ( $name = “Ben” )<br />  #set ( $clock = “${size}Tall$name” )<br />  The clock is $clock.<br />输出结果是：The clock is BigTallBen.。使用这种格式主要是为了使得$size不被解释为$sizeTall。<img src ="http://www.blogjava.net/chenleiyu/aggbug/37570.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-27 11:23 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/27/37570.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在以前的公司里没怎么用过Tiger,现在公司大量地使用新技术,没办法，自己看了，下面是个关于泛型的</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/09/34433.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Thu, 09 Mar 2006 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/09/34433.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/34433.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/09/34433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/34433.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/34433.html</trackback:ping><description><![CDATA[<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">&nbsp;com.brainysoftware.jdk5.app16;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.List;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.ArrayList;<br></span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;GenericListTest&nbsp;{　<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;main(String[]&nbsp;args)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;　　</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;in&nbsp;JDK&nbsp;1.4　　List&nbsp;stringList1&nbsp;=&nbsp;new&nbsp;ArrayList();　</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;　stringList1.add(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Java&nbsp;1.0&nbsp;-&nbsp;5.0</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringList1.add(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">without&nbsp;generics</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;cast&nbsp;to&nbsp;java.lang.String　　String&nbsp;s1&nbsp;=&nbsp;(String)&nbsp;stringList1.get(0);　</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;　System.out.println(s1.toUpperCase());　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;now&nbsp;with&nbsp;generics&nbsp;in&nbsp;JDK&nbsp;5　　</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;stringList2&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ArrayList</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">();　　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringList2.add(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Java&nbsp;5.0</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　　stringList2.add(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">with&nbsp;generics</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;no&nbsp;need&nbsp;for&nbsp;type&nbsp;casting　　</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;s2&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;stringList2.get(</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">);　　<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(s2.toUpperCase());　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">不再需要强制类型转换<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">下面是MAP的</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">&nbsp;com.brainysoftware.jdk5.app16;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.HashMap;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.Map;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;MapTest&nbsp;{　<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;main(String[]&nbsp;args)&nbsp;{　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　Map</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String,&nbsp;String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;map&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;HashMap</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">String,&nbsp;String</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">();　　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">key1</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">value1</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　　map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">key2</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">value2</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;value1&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;map.get(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">key1</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);　<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><br><img src ="http://www.blogjava.net/chenleiyu/aggbug/34433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-09 11:00 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/09/34433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于Java的全文索引引擎Lucene简介</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33851.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 06 Mar 2006 05:52:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33851.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/33851.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/33851.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/33851.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ucene是一个基于Java的全文索引工具包。<br><br>基于Java的全文索引引擎Lucene简介：关于作者和Lucene的历史<br>全文检索的实现：Luene全文索引和数据库索引的比较<br>中文切分词机制简介：基于词库和自动切分词算法的比较<br>具体的安装和使用简介：系统结构介绍和演示<br>Hacking Lucene：简化的查询分析器，删除的实现，定制的排序，应用接口的扩展<br>从Lucene我们还可以学到什么<br>基于Java的全文索引/检索引擎——Lucene<br><br>Lucene不是一个完整的全文索引应用，而是是一个用Java写的全文索引引擎工具包，它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/03/06/33851.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/33851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-06 13:52 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/06/33851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我们需要重新思考Model2的价值</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33805.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 06 Mar 2006 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33805.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/33805.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33805.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/33805.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/33805.html</trackback:ping><description><![CDATA[
为什么我说Struts/WebWork会受到Ajax的威胁呢？有的人可能觉得大家相安无事不是很好，你是不是有神经病故意挑起人民内部矛盾？问题是他们之间确实存在着一些深层的内在矛盾和冲突，这些矛盾才是目前Struts和
WebWork都只能在非常有限的程度上支持Ajax的原因。所以，问题是架构性的，并不是小型的修补或者更好的编程技巧可以彻底解决的。<p class="paragraph">传统的服务器端MVC架构设计（也就是Model2），存在着一个基本的假设就是Web应用的工作流是由一系列的页面切换构成的。这种架构中的一个View，从语义上来讲只能代表一个完整的HTML页面。整个Web应用的表现层，被划分成为非常多的页面的组合。</p><p class="paragraph">而Ajax
开发者眼里，Web应用的工作流并不是这样构成的。Ajax开发者看待Web应用的角度与传统开发者相比差别非常大。在一个Ajax应用中，只有相对很少
的页面。每个页面，包括页面引用CSS样式、JS脚本，都是一个更小型的Ajax应用。甚至一些功能简单的Ajax应用，本身仅仅由一个单一的页面构成。
例如一个简单的RSS阅读器，还有IBM笔记本上那个获得天气预报的桌面。<br>
按照Ajax in Action，Ajax应用可以分成3种类型：
</p><ol><li>以内容为中心的应用，服务器返回的是一段HTML内容。</li><li>以脚本为中心的应用，服务器返回的是一段JS脚本。</li><li>以数据为中心的应用，服务器返回的是一段数据，可以是XML格式、JSON格式或者其他文本格式。</li></ol>服务器返回给Ajax应用的3种类型的网络流量（不称为数据是与上面第3种Ajax应用相区别），任何一种都不能被简单地视作传统MVC架构中
的View，因为他们各自所代表的语义与传统MVC架构中的View的语义是完全不同的。所以可以看出，除了初次交付给浏览器一个完整的Ajax应用之
外，传统的MVC架构对于Ajax应用的支持是非常有限的。其实为了给客户端提供上面3类网络流量，一个Servlet已经足够了。DWR、JSON-
RPC、Buffalo在服务器端也就是由Servlet实现的，不要求服务器端一定要安装某种MVC框架。<br>
上面3类应用，前面的两类，客户端JS代码比较简单，表现逻辑仅有一部分位于客户端，大部分仍然位于服务器端，因此传统的服务器端MVC架构仍然是非常有
价值的。但是大家注意第3类Ajax应用，实际上它已经将绝大部分甚至可以将全部的表现逻辑都转移到客户端来执行，这个时候服务器端传统的Web表现层实
际上被架空了（皮之不存，毛将焉附？）。而对于Ajax应用来说，虽然近期可能还是以第1类Ajax应用为主（例如，所谓的AHAH技术），但是最有生命
力和发展前景的还是第3类Ajax应用。<p class="paragraph">自
从1999年M$推出IE5.0支持XMLHTTP，可以不刷新页面以异步方式从服务器获取数据之后，Web开发的领域就埋下了一颗定时炸弹（6年以后，
一个新词Ajax的出现引爆了这颗炸弹）。Model2最初的设计应该发生在这件大事（现在应该承认，M$做了一件天大的好事）发生之前，其设计师不可能
想到异步请求的价值。按照Model2的设计思想直接产生了Struts。但是后来的WebWork在最初设计阶段仍然与这个技术失之交臂，这是相当可惜
的一件事情。WebWork其实最初设计的时候就可以走的更远，但是他们只想超越Struts，做一个更好的Model2
MVC开发框架。现在他们再想赶上这班列车已经有点晚了。如果基础的服务器端MVC架构的价值是可疑的，那么其他围绕这个架构所开发的基础架构的价值也同
样是可疑的。</p>所以在现在这个时刻，重新正本清源地思考Model2最初的设计，它带来的Web开发的巨大进步，以及它所存在的不足，是一个非常现实的问题。<img src ="http://www.blogjava.net/chenleiyu/aggbug/33805.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-06 10:37 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/06/33805.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Thinking in AJAX</title><link>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33803.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 06 Mar 2006 02:26:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33803.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/33803.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/03/06/33803.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/33803.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/33803.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 摘要:<br>众所周知，异步交互、JavaScript脚本和XML封装数据是AJAX的三大特征。其实，在实际应用中，不需要牢牢套死这三条大律，在我看来，AJAX - X，即去掉用XML封装数据，也不失为一种好的设计思路，如果应用恰当，更显轻盈步伐和巧妙思路。<br><br>一般读取AJAX返回的XML结构的数据时使用XMLHttp的responseXML对象属性，同时，XMLHttp也提供了另外一个属性，即  ResponseText，通过这个属性，XMLHttp可以接受来自服务器的文本结构的字符串信息。去掉XML的AJAX可以使用  ResponseText这个对象属性，很灵活的操控返回数据的格式，可以自定义格式，比如我通常喜欢用c语言的那种文件流方式定义返回的字符串结构，有 文件头和具体的文件信息实体，文件头分为状态信息以及文件字符长度，我摒弃了文件字符长度的定义，规定死接受的ResponseTex字符串中的第一位为 状态码，比如设定常量值0表示一起正常，非0的数字表示不正常，甚至有错误等。如果有非0值，程序自动取第二位起到257位(长度为256)的字符串组成 为状态信息，从&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/03/06/33803.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/33803.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-03-06 10:26 <a href="http://www.blogjava.net/chenleiyu/archive/2006/03/06/33803.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>当Hibernate遇上Spring</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32859.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 28 Feb 2006 08:00:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32859.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32859.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32859.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 软件系统通常由多个组件构成，每个组件负责一个特定的功能领域。但是，这些组件也经常承担它们的核心功能之外的额外责任。系统服务（例如日志、事务管理和安全性）经常发现自己跑到了别的组件的领域里，而这些组件的核心职责是其他事情。结果就是所谓的“代码纠缠”，或者更简单点儿说“一团糟”。面向方面编程是一种试图解决这个问题的编程技术，它把关注点的隔离提升为核心的编程概念。<br>　　<br>　　使用 AOP 时，仍然是在一个地方定义系统的公共功能，但是可以声明性地定义 如何 和 在哪里 应用这个功能。如果对横切关注点（例如日志和事务管理）进行了模块化，那么不用修改每个单独的类，就可以向代码中添加新特性。这类模块化的关注点称作 方面。&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/02/28/32859.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/32859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-28 16:00 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/28/32859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse快捷键大全</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32845.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Tue, 28 Feb 2006 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32845.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32845.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/28/32845.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32845.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32845.html</trackback:ping><description><![CDATA[<TD><SPAN id=ArticleContent1_ArticleContent1_lblContent>Ctrl+1 快速修复(最经典的快捷键,就不用多说了)<BR>Ctrl+D: 删除当前行 <BR><SPAN id=ArticleContent1_ArticleContent1_lblContent>Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性<BR><SPAN id=ArticleContent1_ArticleContent1_lblContent>Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)<BR>Shift+Ctrl+Enter 在当前行插入空行(原理同上条)<BR><SPAN id=ArticleContent1_ArticleContent1_lblContent>Ctrl+Q 定位到最后编辑的地方<BR>Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)<BR>Ctrl+M 最大化当前的Edit或View (再按则反之)<BR>Ctrl+/ 注释当前行,再按则取消注释<BR>Ctrl+O 快速显示 OutLine<BR>Ctrl+T 快速显示当前类的继承结构<BR>Ctrl+W 关闭当前Editer<BR>Ctrl+K 参照选中的Word快速定位到下一个<BR>Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)<BR><SPAN id=ArticleContent1_ArticleContent1_lblContent>Ctrl+/(小键盘) 折叠当前类中的所有代码<BR>Ctrl+×(小键盘) 展开当前类中的所有代码<BR><SPAN id=ArticleContent1_ArticleContent1_lblContent>Ctrl+Shift+F 格式化当前代码<BR></SPAN></SPAN></SPAN></SPAN></SPAN><SPAN><SPAN><SPAN><SPAN><SPAN id=ArticleContent1_ArticleContent1_lblContent>Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></TD><img src ="http://www.blogjava.net/chenleiyu/aggbug/32845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-28 14:22 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/28/32845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何使用Ajax开发Web应用程序(1)</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32600.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Feb 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32600.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32600.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32600.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32600.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32600.html</trackback:ping><description><![CDATA[在过去，由于为了获得新数据而不得不重新加载web页面（或者加载其他页面）导致web应用程序发展被限制。虽然有其他方法可用（不加载其他页面），但是
这些技术都没有被很好地支持而且有bug成灾的趋向。在过去的几个月里，一个过去并不被广泛支持的技术已经被越来越多的web冲浪者（web
surfers??是指浏览器还是浏览者？）所接受，它给了开发者更多的自由开发先进的web应用程序。这些通过javascript来异步取得xml数
据的应用程序，被亲切的称为“Ajax应用程序”（Asynchronous Javascript and XML
applications）。在这篇文章中，我将会解释如何通过Ajax来取回一个远程的XML文件并更新一个web
page，并且随着这个系列的继续，我将讨论更多的方法，使用ajax技术将你的web应用程序提升到一个新的层次.
<br> 
<br> 　　这第一步就是创建一个带一些数据的XML文件。我们将这个文件命名为data.xml。它是一个简单的XML文件，而在一个真实的程序中，它会复杂许多，但对于我们的例子来说，简单明了是最合适地。
<br> 
<br>  <root> <data> 这是一些示例数据，它被保存在一个XML文件中，并被JavaScript取回。 </data> </root>
<br> 
<br> 　　现在让我们创建一个简单的web页面包含一些示例数据。这个页面将是我们的js脚本所在，并且这个页面将会让用户们访问柄看到Ajax脚本的运行。我们把它命名为ajax.html
<br> 
<br> <br>   "http://www.w3.org/TR/html4/strict.dtd"&gt;
<br> 
<br>   
<br>     
<br>     
<br>   <br>   
<br>     <h1>使用ajax开发web应用程序</h1>
<br>     <p>这个页面演示了AJAX技术如何通过动态读取一个远程文件来更新一个网页的内容－－不需要任何网页的重新加载。注意：这个例子对于禁止js的用户来说没有效果。</p>
<br>     <p id="xmlObj">
<br>     这是一些示例数据，它是这个网页的默认数据 <a href="http://chenleiyu.blog.edu.cn/data.xml"><br>     title="查看这个XML数据." onclick="ajaxRead('data.xml'); this.style.display='none'; return false"&gt;查看XML数据.</a>
<br>     </p>
<br>   <br> <br> 
<br>
　　注意，对于那些没有javascript的用户，我们直接链接到data.xml文件。对于那些允许运行javascript的用户，函数
“ajaxRead”将被运行，这个链接被隐藏，并不会被转向到那个data.xml文件。函数“ajaxRead”现在还没定义。所以如果你要检验上面
的示例代码，你会得到一个
javascript错误。让我们继续并定义这个函数（还有其他的），让你能够看到ajax是如何工作的，下面的脚本要放到你的head标签里：
<br> 
<br> <script type="text/javascript"><!--
<br /> function ajaxRead(file){
<br />   var xmlObj = null;
<br />   if(window.XMLHttpRequest){
<br />       xmlObj = new XMLHttpRequest();
<br />   } else if(window.ActiveXObject){
<br />       xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
<br />   } else {
<br />       return;
<br />   }
<br />   xmlObj.onreadystatechange = function(){
<br />     if(xmlObj.readyState == 4){
<br />        updateObj('xmlObj', xmlObj.responseXML.getElementsByTagName('data')[0].firstChild.data);
<br /> 
<br />    }
<br />     }
<br />     xmlObj.open ('GET', file, true);
<br />     xmlObj.send ('');
<br />   }
<br />   function updateObj(obj, data){
<br />    document.getElementById(obj).firstChild.data = data;
<br />   }
<br />   //--></script>
<br> 
<br>
　　这堆代码有点多，让我们一点点的进行。第一个函数叫做“ajaxRead”－也就是我们在页面的“查看XML数据”链接中调用的函数，我们定义了一个
“xmlObj”
变量－这将作为客户端（用户正在查看的这个web页面）以及服务端（web站点本身）之间的中间件。我们在一个if/else块中定义这个对象：
<br> 
<br> if(window.XMLHttpRequest){
<br>    xmlObj = new XMLHttpRequest();
<br> } else if(window.ActiveXObject){
<br>    xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
<br> } else {
<br>    return;
<br> }
<br> 
<br>
　　这只是一个对不同对象是否可用的测试－某些浏览器实现了不同的XMLHttpRequest对象，所以当我们定义“xmlObj”作为我们的
XMLHttpRequest对象时，我们不得不根据浏览器所实现的来定义它。如果没有可用的XMLHttpRequest对象，我们将执行
“return”语句结束这个函数以避免脚本错误。在大部分情况下，这个检验将返回一个XMLHttpRequest对象－这部分代码应该能够在绝大部分
的浏览器上工作，除了少部分比较老的浏览器的异常情况（它能够工作在ie5.01上，但是在netscape4上会使函数终止）。
<br> 
<br> 　　接下来是这些代码块:
<br> 
<br> xmlObj.onreadystatechange = function(){
<br>   if(xmlObj.readyState == 4){
<br>       updateObj('xmlObj', xmlObj.responseXML.getElementsByTagName('data')[0].firstChild.data);
<br>   }
<br> }
<br> 
<br> 　　每次 XMLHttpRequest的状态发生变化，事件“onreadystatechange”就会被触发。通过使用
“xmlObj.onreadystatechange =
function(){...}”我们能够创建一个函数并让它在这个XMLHttpRequest对象的状态每次发生改变的时候立刻运行。这里总共有五个
状态，由0走到4。
<br> 
<br> 　　0 – 尚未初始化（在这个XMLHttpRequest开始前）
<br> 
<br> 　　1 – 加载（XMLHttpRequest初始化一结束）
<br> 
<br> 　　2 – 加载结束（XMLHttpRequest一从服务器上获得一个回应）
<br> 
<br> 　　3 – 交互（当XMLHttpRequest对象和服务器连接中）
<br> 
<br> 　　4 – 结束（当XMLHttpRequest被告知它已经完成了所有人物并结束运行）
<br> 
<br>
　　这第五个状态（数字4）就是我们能够确定数据已经可用的标志，所以我们检验这个xmlObj.readyState是否等于“4”来确定数据是否可
用，如果是4，我们运行updateObj函数。这个函数带两个参数：一个当前web页面的元素ID（当前web页面中要更新的元素）以及用于填充这个元
素的数据。这个函数的运行方式在稍后将更详细地解释。
<br> 
<br> 　　我们的web页面的p元素有一个id“xmlData”，这就是我们准备更新的段落。我们正在取得的数据来自于XML文件，但它有点复杂。这里是它如何工作的原理。
<br> 
<br> 　　 xmlObj.responseXML属性是一个DOM对象
－它很象“document”对象，除了它来自远程的XML文件。换句话说，如果你在data.xml中运行脚本，那
xmlObj.responseXML
就是一个“document”对象。因为我们知道这些，我们能够通过“getElementsByTagName”方法取得任何XML节点。数据包含在一
个命名为“<data>”的XML节点中，所以我们的任务很简单：取得第一个（而且只有这一个）数据节点。因而， xmlObject.responseXML.getElementsByTagName("data")[0]返回XML文件中的第一个&lt; data&gt;节点。
<br> 
<br> 　　注意：它返回的是XML节点，而不是节点中的数据－这个数据必须通过访问XML节点的属性取得，这就是下一步要说的。
<br> 
<br> 　　接下来，取得数据只需要简单的指定“firstChild.data”（firstChild指向了那个被<data>节点包含的文本节点，而这个“data”属性则是这个文本节点的实际文本）。
<br> xmlObj.open ('GET', file, true);
<br> xmlObj.send ('');
<br> 
<br>
　　这是我们的ajaxRead函数的最后一个部分。它说了些什么？嗯，xmlObj的这个“open”方法打开了一个到服务器（通过一个指定的协议，这
里指定的是“GET”－你可以使用“USE”或者其他别的协议）的连接，去请求一个文件（在我们的例子里，变量“file”被作为一个参数赋给
ajaxRead函数－data.xml），而且javascript可以同步（false）或者异步（true，默认值）的处理请求。由于这是异步的
Javascript和XML（AJAX），我们将使用默认的异步方式－在这个例子中，使用同步方式将不起作用。
<br> 
<br>
　　这是我们函数中的最后一行，它简单的发送一个空字符串回服务器。如果没有这行，xmlObj的readyState永远不会到4，所以你的页面永远不
会更新。这个send方法能够用于作其他事情，但今天我只是用来从服务器上取得数据－并不发送它－所以在这篇文章中我不准备介入任何关于send方法的细
节。
<br> 
<br> function updateObj(obj, data){
<br>   document.getElementById(obj).firstChild.data = data;
<br> }
<br> 
<br>
　　现在再稍微解释一下updateObj函数：这个函数使用一个新的值来更新当前页面上任何指定的元素。他的第一个参数，“obj”是当前页面中元素的
ID－那个要被更新的对象；它的第二个参数，“data”是用来将那个将被替换值的对象（“obj”）的内容替换掉。一般来说，检验一下并确定当前页面上
确实有一个元素的ID是“obj”是比较明智的，但对我们的脚本的这个隔离级别来说校验并不必要。这个函数更新的方式和我们之前从XML文件的
“data”节点取得数据的方式类似－它定位它要更新的元素（这时候这个元素的ID代替了它的标签名和在页面中的索引）并设置这个元素的第一个子节点（文
本节点）的data属性为新的值。如果你需要使用HTML而不是纯文本来更新一个元素，你也可以使用
<br> 
<br> document.getElementById(obj).innerHTML = data
<br> 
<br> 　　这就是全部了
<br> 
<br>
　　这个概念很简单，而且代码也不是很难。你能够从某个地方读取一个文件并且不需要重新加载这个web页面。你有足够的灵活性来作各种事情，包括从表单发
送数据（不需要重新加载web页面）并且使用一个服务端语言来动态生成XML文件。如果你需要更近一步，记得这个连接是很有用的－哦，还要记得
Google是你朋友。在另外的文章中，我将解释你如何配合服务端技术使用AJAX来构造强大的web应用程序。</data></data><img src ="http://www.blogjava.net/chenleiyu/aggbug/32600.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-27 11:23 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/27/32600.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Java Server Faces建立交互式WEB站点</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32599.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Feb 2006 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32599.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32599.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32599.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32599.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32599.html</trackback:ping><description><![CDATA[在创建交互式网络应用方面，Java Server Faces(以下建成为JSF)相对于诸如Java Server Pages或Apache
Struts等同类技术而言拥有很多优势。JSF十分清晰地分离了应用逻辑层与用户界面表示层，提高了网络应用软件的维护能力，JSF同时还提供了一个架
构用于开发和重用网络用户界面组件。
　　很多网络应用程序的开发者正在转移到JSF上来，但他们同时也发现预定义的JSF用户界面组件受到DHTML功能的限制。一些高级应用，例如监控或商
务进程监测，需要和JSF架构兼容的高级视觉组件。
　　在JSF架构的标准之上开发自定义的用于网络图形用户界面的组件是一件轻松的事情，这些组件可以被应用程序员重用。而且，网络组件的开发者现在可以提
供更加复杂的组件，同时可以保证开发者可以轻松的享受这些组件带来的优势和便利。这些JSF用户界面组件必须简洁地整合与部署到JSF运行时架构，而且在
应用软件设计过程中，还要较好地整合到提供JSF支持的集成开发环境(IDE)中
　　除了JSF自带的基本的用户界面架构之外，在第一次开发自定义组件的时候，开发者还会遇到一些缺陷和障碍。在这篇文章当中，我们将要介绍如何构建图形
化的JSF组件，这种组件使用纯HTML是非常难以实现的。图形化JSF组件的特色之处在于，不仅需要生成
DHTML，而且还需要一些额外的图形生成与客户端交互的支持。我们将使用一个图表组件的例子来演示，这个图表组件是用来提供图表和各种客户端的浏览与交
互功能。最后，将展示如何将图表组件整合到支持JSF的集成开发环境中。在理解了这个图表组件的设计过程之后，开发者将对如何实现图形化的JSF组件有更
深入的理解，希望能够对他们开发自定义的JSF图形组件有所帮助。
　　什么是Java Server Faces?
　　JSF是一个标准的服务器端架构，用于简化网络应用软件表示层的构建。开发人员可以组装这些可复用的用户界面组件来创建网页，将这些组件绑定到应用程
序的数据源，并利用服务器端的事件处理器来处理客户端事件。依据JSF规范，组件开发者所制作的组件可以简洁地整合到
JSF运行时架构，而且在应用软件设计过程中，还可以整合到与JSF兼容的集成开发环境中。JSR
127规范定义了这样的JSF架构，还同时提供了比如输入栏和按钮等基本用户界面组件的可供参考的实现。JSF组件中的绝大部分都符合HTML
2.0标准中的HTML组件和标签的规范。这些相对简单的组件对于很多网络应用程序来讲已经足够了。
　　然而，很多应用程序，比如监控或监测系统，需要更复杂的数据表示与交互，例如图表、图示和映射等。因为在HTML
中直接生成复杂图形的能力有限，所以设计这些高级组件也并不直观。一种解决方案就是让服务器端的组件将图片传送到客户端，然而，这会带来它自身的问题因为
最基本的HTML的图片交互功能是很有限的。所以，最后必须使用JavaScript来实现用户的数据浏览与交互功能。
　　创建简单的JSF组件
　　本文将这部分将描述一个非常简单的JSF组件的开发步骤，这个组件的功能是将CSS导入到一个HTML的网页上。这个简单组件的描述和代码将作为基础
知识，为下一部分继续讲解高级JSF图表组件打下基础。
　　图1 展示了如何使用组件及其结果
　　图1
　　使用这一组件的好处在于，只要通过JSF动作去改变组件的设定值就能够改变整个页面的外观。
　　一个JSF组件由一些Java类和配置文件组成，为了创建一个自定义的JSF组件，开发人员需要: 1.
　　编写一个扩展JSF基础组件类的Java类 2. 　　为默认的渲染工具编写一个渲染器 3.
　　编写一个Java类来描述标签，这个标签将用于JSP页面 4. 　　编写一个标签库定义文件 5. 　　编写一个JSF配置文件
　步骤1:开发组件的Java类
　　组件类将负责管理代表组件状态的属性，因此，我们必须根据组件的行为(如输入组件或输出组件)，为组件选择适当的基类。
　　在列表A中描述的组件扩展了javax.faces.component.UIOutput，以显示指向某个样式表文件的URL，或内联式样式表的内
容。
　　列表A
import javax.faces.component.*;
public class CSSComponent extends UIOutput {
private Boolean link;
public String getFamily() {
return "faces.CSSFamily";
}
public boolean isLink() {
if (link != null)
return link.booleanValue();
ValueBinding vb = getValueBinding("link");
if (vb != null) {
Boolean bvb = (Boolean) vb.getValue(FacesContext.
getCurrentInstance());
if (bvb != null)
return bvb.booleanValue();
}
return false;
}
public void setLink(boolean link) {
this.link = new Boolean(link);
}
public Object saveState(FacesContext context) {
return new Object[] { super.saveState(context), link };
}
public void restoreState(FacesContext context,
Object stateObj) {
Object[] state = (Object[]) stateObj;
super.restoreState(context, state[0]);
link = (Boolean) state[1];
}
}
　　代码中“关联”的属性规定了值的类型:要么是一个URL，要么是内联样式。该组件还必须能够在向服务器发送请求期间，使用经过JSF架构处理过的对
象，来存储并恢复自己的状态。JSF架构自动调用saveState和restoreState方法，我们可以在组件中实现这两种方法来达到这一目标。
　　步骤2:编写渲染器
　　渲染程序有两个作用。首先，渲染程序负责发送适当的HTML程序段，该程序段能在客户端中渲染组件。通常情况下，这个HTML程序段由一些适于渲染整
个网络浏览器的HTML标签组成，这个渲染阶段还能发送增强客户端交互性的JavaScript代码。
　　渲染程序的第二个作用是对来自客户端的数据进行解码，从而对服务器端的组件状态进行更新(比如用户在文本字段输入的文本)。标准渲染程序软件包具有强
制 性，但也可以提供其他渲染程序软件包，用于提供其他的客户端表示方法或SVG之类的语言。
　　通过检验组件的链接属性，在列表2中实现的渲染程序将选择在HTML页面中发送的CSS样式类别。
　　列表B
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class CSSRenderer extends Renderer {
public void encodeEnd(FacesContext context,
UIComponent component)
throws IOException {
super.encodeEnd(context, component);
if (component instanceof CSSComponent) {
CSSComponent cssComponent =
(CSSComponent) component;
String css = (String)cssComponent.getValue();
boolean isLink = cssComponent.isLink();
if (css != null)
if (isLink)
context.getResponseWriter().write("");
else
context.getResponseWriter().write("\n" + css
+ "\n\n");
}
}
} 步骤3：编写标签类 同样，JSF架构提供了用于扩展的基类，来编写与组件相关的标签。该标签类将负责： *
定义将在faces-config.xml文件中应用的组件类型和渲染类型，我们将在下一部分具体介绍这个XML文件。 *
创建JSF组件（由JSF架构来处理）并传递JSF标签中所包含的属性来初始化组件。 *
在列表C中的标签提供了setter和getter来管理链接和值的属性。
列表C
import javax.faces.Webapp.UIComponentTag;
public class CSSTag
extends UIComponentTag {
private String value;
private String link;
public String getComponentType() {
return "faces.CSSComponent";
}
public String getRendererType() {
return "HTML.LinkOrInlineRenderer";
}
protected void setProperties(UIComponent component)
{
super.setProperties(component);
Application app = getFacesContext().getApplication();
if (value != null)
if (isValueReference(value))
component.setValueBinding("value",
app.createValueBinding(value));
else
component.getAttributes().put("value", value);
if (link != null)
if (isValueReference(link))
component.setValueBinding("link",
app.createValueBinding(link));
else
component.getAttributes().put("link",
new Boolean(link));
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
} 组件一旦创建，便会调用setPropertie方法，对标签属性进行初始化。每个标签属性要么是文字值，要么是bean属性的一个绑定。
步骤4：编写一个标签库定义（TLD）
TLD是一个XML文件，它通过将标签名与相应的Java类相关联来描述标签。TLD还描述了标签所允许的属性。
在列表D中的这个TLD定义了一个名为“css”的标签，该标签被绑定到CSSTag类。它还声明了链接和值标签的属性。
列表D

<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>custom</short-name><img src ="http://www.blogjava.net/chenleiyu/aggbug/32599.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-27 11:22 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/27/32599.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax 的 Java 对象序列化(1)</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32598.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Feb 2006 03:21:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32598.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32598.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32598.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32598.html</trackback:ping><description><![CDATA[如果您正在使用异步&nbsp;JavaScript&nbsp;和&nbsp;XML(Ajax)进行&nbsp;Java™&nbsp;Web&nbsp;开发，那么您最关心的问题可能就是把数据从服务器传递给客
户机。在面向&nbsp;Java&nbsp;开发人员的&nbsp;Ajax&nbsp;系列的文章中，Philip&nbsp;McCarthy&nbsp;介绍了&nbsp;Java&nbsp;对象序列化的五种方式，并提供了选择
最适合应用程序的数据格式和技术所需要的全部信息。本文将侧重于许多&nbsp;Java&nbsp;Web&nbsp;开发人员最关心的问题:为客户机生成数据。&nbsp;<br><br>多
数&nbsp;Java&nbsp;开发人员已经把模型-视图-控制器(MVC)模式应用在他们的&nbsp;Web&nbsp;应用程序上。在传统的&nbsp;Web&nbsp;应用程序中，视图组件由JSP&nbsp;或
者其他表示技术(例如&nbsp;Velocity&nbsp;模板)构成。这些表示组件动态地生成全新的&nbsp;HTML&nbsp;页面，替代用户以前正在查看的页面，从而更新用户界面。
但是，在&nbsp;Java&nbsp;Web&nbsp;应用程序使用&nbsp;Ajax&nbsp;UI&nbsp;的情况下，基于从&nbsp;XMLHttpRequest&nbsp;的响应接收到的数据，
JavaScript&nbsp;客户端代码对于更新用户看到的内容负有最终责任。从服务器的角度来看，视图成为它响应客户机请求而发送的数据表示。<br><br>这
篇文章侧重于可以用来生成&nbsp;Java&nbsp;对象以数据为中心的视图的技术。我将演示可以把&nbsp;JavaBeans&nbsp;变成&nbsp;XML&nbsp;文档的各种方法，并且讨论每种
方法的优劣。您将看到为什么&nbsp;XML&nbsp;并不总是最好的途径:对于简单的&nbsp;Ajax&nbsp;请求来说，传输纯文本更好。最后，我将介绍&nbsp;JavaScript&nbsp;对
象标注(JSON)。JSON&nbsp;允许数据以序列化的&nbsp;JavaScript&nbsp;对象图的形式传输，在客户端代码中处理序列化的&nbsp;JavaScript&nbsp;对象
图极为容易。<br><br>关于示例<br><br>我将使用一个示例应用程序和几个用例来演示这里讨论的技术特性和技术。图&nbsp;1&nbsp;显示的极为简单的数据模型可以表示示例用例。这个模型代表在线商店中的顾客帐户。顾客拥有以前订单的集合，每个订单包含几个商品。<br>
<p align="center"><img src="http://www.lawfan.com/20051123/ajax/images/200611647448.jpg" onclick="javascript:window.open(this.src);" style="cursor: pointer;" onload="javascript:if(this.width>500)this.style.width=500;" height="113" width="477"></p>
<p align="center">图 1. 简单的对象模型</p>
<p align="left">虽然&nbsp;XMLHttpRequest&nbsp;对于发送数据使用的格式没有做任何限制，但是对于多数目的来说，只发送传统的表单
数据是适合的，所以我的讨论集中在服务器的响应上。响应也可以有基于文本的格式，但是正如它的名字表示的，XMLHttpRequest&nbsp;具有内置的处理
&nbsp;XML&nbsp;响应数据的能力。这使&nbsp;XML&nbsp;成为&nbsp;Ajax&nbsp;响应的默认选择，所以我们从&nbsp;XML&nbsp;格式开始讨论。<br><br>从&nbsp;Java&nbsp;类产生&nbsp;XML<br><br>把&nbsp;Ajax&nbsp;
响应作为&nbsp;XML&nbsp;来传递有许多原因:每个支持&nbsp;Ajax&nbsp;的浏览器都有导航&nbsp;XML&nbsp;文档的方法，也有许多服务器端技术可以处理&nbsp;XML&nbsp;数据。通过制
定一个方案，描述要交换的文档类型，在&nbsp;Ajax&nbsp;客户端和服务器端之间很容易定义合约，而且如果服务器端架构采用面向服务的方式，那么使用&nbsp;XML&nbsp;也
可以允许非&nbsp;Ajax&nbsp;客户机使用您提供的数据。<br><br>我将考虑从&nbsp;Java&nbsp;对象产生&nbsp;XML&nbsp;数据的三种方法，并讨论每种方法的优劣。<br><br>自行进行序列化<br><br>首
先，可以从对象图以编程的方式生成&nbsp;XML。这种方式可以简单到只是在每个&nbsp;JavaBean&nbsp;类中实现&nbsp;toXml()&nbsp;方法即可。然后就可以选择合适
的&nbsp;XML&nbsp;API，让每个&nbsp;bean&nbsp;提供表示自己状态的元素，并递归地对自己的成员调用对象图。显然，这种方式无法扩展到大量的类，因为每个类都需要
专门编写自己的&nbsp;XML&nbsp;生成代码。从好的方面来看，这是一个实现起来简单的方式，没有额外的配置支出或者更复杂的构建过程支出，任何
&nbsp;JavaBean&nbsp;图都可以只用几个调用就变成&nbsp;XML&nbsp;文档。<br><br>在前一篇文章的示例代码中，我把&nbsp;XML&nbsp;标记字符串连接在一起，实现
了&nbsp;toXml()&nbsp;方法。上次我就提到过，这是个糟糕的方法，因为它把确保标记配对、实体编码等工作的负担放在每个&nbsp;toXml()&nbsp;方法的代码中。在
&nbsp;Java&nbsp;平台上有几个&nbsp;XML&nbsp;API&nbsp;可以替您做这些工作，这样您就可以把精力集中在&nbsp;XML&nbsp;的内容上。清单&nbsp;1&nbsp;用&nbsp;JDOM&nbsp;API&nbsp;实现了
在线商店示例中表示订单的类中的&nbsp;toXml()(请参阅&nbsp;图&nbsp;1)。<br>清单&nbsp;1.&nbsp;Order&nbsp;类的&nbsp;toXml()&nbsp;的&nbsp;JDOM&nbsp;实现&nbsp;</p>
<p><font style="background-color: rgb(221, 221, 221);">public Element toXml() {</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp; Element elOrder = new Element("order");<br><br>elOrder.setAttribute("id",id);</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp; elOrder.setAttribute("cost",getFormattedCost());</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp; Element elDate = new Element("date").addContent(date);<br><br>elOrder.addContent(elDate);</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp; Element elItems = new Element("items");<br><br>for (Iterator&lt;Item&gt; iter = <br><br>items.iterator() ; iter.hasNext() ; ) {<br><br>elItems.addContent(iter.next().toXml());<br><br>}<br><br>elOrder.addContent(elItems);</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp; return elOrder;<br><br>}</font></p>
<p>在这里可以看到用 JDOM 创建元素、使用属性和添加元素内容有多么简单<font color="#333333">。</font>递归地调用复合 JavaBean 的 toXml() 方法是为了取得它们子图的 Element 表示<font color="#333333">。</font>例如，items 元素的内容是通过调用 Order 聚合的每个 Item 对象上的 toXml() 得到的<font color="#333333">。</font></p>
<p>一旦所有的 JavaBean 都实现了 toXml() 方法，那么把任意对象图序列化成 XML 文档并返回给 Ajax 客户机就简单了，如清单 2 所示<font color="#333333">。</font></p>
<p>清单 2. 从 JDOM 元素生成 XML 响应</p>
<p><font style="background-color: rgb(221, 221, 221);">public void doGet(HttpServletRequest req, HttpServletResponse res)<br><br>throws java.io.IOException, ServletException {</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp;&nbsp;&nbsp; String custId = req.getParameter("username");<br><br>Customer customer = getCustomer(custId);</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp;&nbsp;&nbsp; Element responseElem = customer.toXml();<br><br>Document responseDoc = new Document(responseElem);</font></p>
<p><font style="background-color: rgb(221, 221, 221);">&nbsp;&nbsp;&nbsp; res.setContentType("application/xml");<br><br>new XMLOutputter().output(responseDoc,res.getWriter());<br><br>}</font></p>
<p>JDOM 再次把工作变得非常简单<font color="#333333">。</font>只需要在对象图返回的 XML 元素外面包装一个 Document，然后用 XMLOutputter 把文档写入 servlet 响应即可<font color="#333333">。</font>清单 3 显示了用这种方式生成的 XML 示例，用 JDOM Format.getPrettyFormat() 对 XMLOutputter 进行初始化，格式化得非常好<font color="#333333">。</font>在这个示例中，顾客只做了一个订单，包含两个商品<font color="#333333">。</font></p>
<p>
</p><div class="guanggao"><span id="contentAdv"></span></div>

<p>清单 3. 代表顾客的 XML 文档 </p>

<p><font style="background-color: rgb(221, 221, 221);">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br><br>&lt;customer username="jimmy66"&gt;<br><br>&lt;realname&gt;James Hyrax&lt;/realname&gt;<br><br>&lt;orders&gt;<br><br>&lt;order id="o-11123" cost="$349.98"&gt;<br><br>&lt;date&gt;08-26-2005&lt;/date&gt;<br><br>&lt;items&gt;<br><br>&lt;item id="i-55768"&gt;<br><br>&lt;name&gt;Oolong 512MB CF Card&lt;/name&gt;<br><br>&lt;description&gt;512 Megabyte Type 1 CompactFlash card. <br><br>Manufactured by Oolong Industries&lt;/description&gt;<br><br>&lt;price&gt;$49.99&lt;/price&gt;<br><br>&lt;/item&gt;<br><br>&lt;item id="i-74491"&gt;</font></p><img src ="http://www.blogjava.net/chenleiyu/aggbug/32598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-27 11:21 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/27/32598.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax优于JSF的原因</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32597.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Feb 2006 03:17:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32597.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32597.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32597.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32597.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: sun为什么会搞出一个JSF，JSF为什么会是现在这个样子，我想原因是这样的。<br>首先，基于组件的Web开发将来会是一个趋势。自包含的组件便于IDE的处理，可以提高开发效率。<br>就是说JSF优于Struts/WebWork这类MVC框架的优势，在于它可以与IDE结合来自动生成代码。<br>而传统的纯手工编写的MVC框架，影响了开发效率。&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/02/27/32597.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/32597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-27 11:17 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/27/32597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AJAX技术汇总</title><link>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32595.html</link><dc:creator>挚爱.NET 情人J2EE</dc:creator><author>挚爱.NET 情人J2EE</author><pubDate>Mon, 27 Feb 2006 03:11:00 GMT</pubDate><guid>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32595.html</guid><wfw:comment>http://www.blogjava.net/chenleiyu/comments/32595.html</wfw:comment><comments>http://www.blogjava.net/chenleiyu/archive/2006/02/27/32595.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/chenleiyu/comments/commentRss/32595.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/chenleiyu/services/trackbacks/32595.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JAX<br>AJAX全称为“Asynchronous JavaScript and XML”（异步JavaScript和XML），是指一种创建交互式网页应用的网页开发技术。它有机地包含了以下几种技术：<br><br>Ajax（Asynchronous JavaScript + XML）的定义<br><br>基于web标准（standards-based presentation）XHTML+CSS的表示；<br>使用 DOM（Document Object Model）进行动态显示及交互；<br>使用 XML 和 XSLT 进行数据交换及相关操作；<br>使用 XMLHttpRequest 进行异步数据查询、检索；<br>使用 JavaScript 将所有的东西绑定在一起。<br>类似于DHTML或LAMP，AJAX不是指一种单一的技术，而是有机地利用了一系列相关的技术。事实上，一些基于AJAX的“派生/合成”式（derivative/composite）的技术正在出现，如“AFLAX”。&nbsp;&nbsp;<a href='http://www.blogjava.net/chenleiyu/archive/2006/02/27/32595.html'>阅读全文</a><img src ="http://www.blogjava.net/chenleiyu/aggbug/32595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/chenleiyu/" target="_blank">挚爱.NET 情人J2EE</a> 2006-02-27 11:11 <a href="http://www.blogjava.net/chenleiyu/archive/2006/02/27/32595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>