<?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-黑灵客栈-随笔分类-脚本语言</title><link>http://www.blogjava.net/mstar/category/1654.html</link><description>搞软件开发就像被强奸,如果不能反抗,就享受它吧！</description><language>zh-cn</language><lastBuildDate>Thu, 31 May 2007 08:47:42 GMT</lastBuildDate><pubDate>Thu, 31 May 2007 08:47:42 GMT</pubDate><ttl>60</ttl><item><title>Flex2 发现之旅：AS3中新的XML处理方法 - E4X</title><link>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 31 May 2007 05:28:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/121136.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2007/05/31/121136.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/121136.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/121136.html</trackback:ping><description><![CDATA[
		<h2 class="post-title">Flex2 发现之旅：AS3中新的XML处理方法 - E4X</h2>
		<div class="tags">
                Tags:<a href="http://www.flexlib.cn/index.php/tag/actionscript" rel="tag">ActionScript</a> <a href="http://technorati.com/tag/ActionScript" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/e4x" rel="tag">E4X</a> <a href="http://technorati.com/tag/E4X" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flash" rel="tag">Flash</a> <a href="http://technorati.com/tag/Flash" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flex" rel="tag">Flex</a> <a href="http://technorati.com/tag/Flex" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a>, <a href="http://www.flexlib.cn/index.php/tag/flex2" rel="tag">Flex2</a> <a href="http://technorati.com/tag/Flex2" rel="tag" class="technorati" target="_blank"><img src="http://www.flexlib.cn/images/icn-talkbubble.gif" alt="[T]" /></a></div>
		<div class="post-content">
				<p class="zh_p">今天我们来看看AS3中新的XML处理方法：<a href="http://www.ecma-international.org/publications/standards/Ecma-357.htm" hreflang="en" target="_blank">E4X</a>，
直到现在，ECMA脚本语言规范（ECMA-262）－－AscriptScript
3.0的核心基础，并没有提供任何的XML数据处理类或方法。AcriontScript之前的版本（从Flash
5中的ActionScript开始）有一些处理XML数据的类和方法，但是它们并不是基于ECMAScript标准的。</p>
				<p class="zh_p">新的ECMA脚本语言规范第4版草稿中定义了一系列新的处理XML数据的类和方法。这些类和方法的集合并命名为E4X（"ECMAScript for XML"），ActionScript 3.0包括如下新的E4X类： <a href="http://livedocs.macromedia.com/labs/1/flex/langref/XML.html" target="_blank">XML</a>、<a href="http://livedocs.macromedia.com/labs/1/flex/langref/XMLList.html" target="_blank">XMLList</a>、 <a href="http://livedocs.macromedia.com/labs/1/flex/langref/QName.html" target="_blank">QName</a>和<a href="http://livedocs.macromedia.com/labs/1/flex/langref/Namespace.html" target="_blank">Namespace</a>。</p>
				<p class="zh_p">E4X类的方法、属性和操作的开法基于以下的目标：</p>
				<ul>
						<li>简单－－E4X尽可能的使得处理XML数据的代码容易编写并且易于理解。</li>
						<li>一致性－－E4X的方法于Actionscript的其它部分协调一致。</li>
						<li>友好－－实用非常好理解的操作符处理XML数据，如点号（.）。</li>
				</ul>
				<p class="zh_p">注意：为避免与E4X中的新的XML类冲突，原来ActionScript 2.0中的XML类在ActionScript 3.0被重命名为<a href="http://livedocs.macromedia.com/labs/1/flex/langref/flash/xml/XMLDocument.html" target="_blank">XMLDocument</a>，为了向前兼容，在ActionScript 3.0中遗留下来的类－－XML、XMLNode、XMLParser和XMLTag--被包含进了<a href="http://livedocs.macromedia.com/labs/1/flex/langref/flash/xml/package-detail.html" target="_blank">flash.xml</a>包中。新的E4X类是核心类－－使用它们不需要import任何包。</p>
				<p class="zh_p">
						<strong>初始化XML对象</strong>
				</p>
				<p class="zh_p">XML对象可以代表一个XML元素、属性、注释、处理指令或文本元素。在ActionScript 3.0中我们可以直接将XML数据赋值给变量：</p>
				<p>
				</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;			</pre>
				<p class="zh_p">你也可以使用new 构造器来从XML数据文本创建一个XML对象实例：</p>
				<p>
				</p>
				<pre>var myXML:XML = new XML("&lt;order&gt;&lt;item id='1'&gt;&lt;menuName&gt;burger&lt;/menuName&gt;&lt;price&gt;3.95&lt;/price&gt;&lt;/item&gt;&lt;/order&gt;")<br /></pre>
				<p class="zh_p">如果XML数据不是格式完好的（如少了结束标签），那么将会出现运行时错误。</p>
				<p class="zh_p">注意，你也可以将变量实例传入XML数据中：</p>
				<pre>var tagname:String = "item";<br />var attributename:String = "id";<br />var attributevalue:String = 5;<br />var content:String = "Chicken";<br />var x:XML = &lt;{tagname} {attributename}={attributevalue}&gt;{content}&lt;/{tagname}&gt;;<br />trace (x.toXMLString())<br />   // Output: &lt;item id="5"&gt;Chicken&lt;/item&gt;</pre>
				<p class="zh_p">通常，我们的应用是从外部源导入XML数据，如web service或RSS feed,以下是一个从远程URL导入XML数据的例子：</p>
				<p>
				</p>
				<pre>var myXML:XML = new XML();<br />var XML_URL:String = "http://www.example.com/Sample3.xml";<br />//创建URLRequest。<br />var myXMLURL:URLRequest = new URLRequest(XML_URL);<br />//使用URLLoader导入数据。<br />var myLoader:URLLoader = new URLLoader(myXMLURL);<br />//添加事件监听器，以在XML数据导入完成后处理XML数据。<br />myLoader.addEventListener("complete", xmlLoaded);<br />//导入完成后，创建使用导入的数据创建XML对象<br />function xmlLoaded(evtObj:Event) {<br />       var myXML:XML = XML(myLoader.data);<br />       trace("Data loaded.");<br /><br />}<br /></pre>
				<p class="zh_p">为了演示代码的清晰性，本文中的大部份示例都第1种直接使用文本的方法创建XML对象。</p>
				<p class="zh_p">E4X包含一些直观的方法XML数据的操作符（如.和@:用于访问属性）：</p>
				<p>
				</p>
				<pre>//获取第1个item的menuName值<br />trace(myXML.item[0].menuName); // Output: burger<br />//获取第1个item的id属性值<br />trace(myXML.item[0].@id);//Output:1<br />//获取id属性为2的item的menuName值<br />trace(myXML.item.(@id==2).menuName); // Output: fries<br />//获取menuName为burger的item的price值<br />trace(myXML.item.(menuName=="burger").price); // Output: 3.95<br /></pre>
				<p class="zh_p">你也可以使用appendChild()方法给XML添加新的子节点：</p>
				<p>
				</p>
				<pre>var newItem:XML =<br />   &lt;item id="3"&gt;<br />       &lt;menuName&gt;medium cola&lt;/menuName&gt;<br />       &lt;price&gt;1.25&lt;/price&gt;<br />   &lt;/item&gt;<br /><br />myXML.appendChild(newItem);<br /></pre>
				<p class="zh_p">当然你也可以使用@和.操作符来更新数据:</p>
				<p>
				</p>
				<pre>myXML.item[0].menuName="regular burger";<br />myXML.item[1].menuName="small fries";<br />myXML.item[2].menuName="medium cola";<br /><br />myXML.item.(menuName=="regular burger").@quantity = "2";<br />myXML.item.(menuName=="small fries").@quantity = "2";<br />myXML.item.(menuName=="medium cola").@quantity = "2";<br /></pre>
				<p class="zh_p">
						<strong>访问XML数据</strong>
				</p>
				<p class="zh_p">你可以使用.（点号）和..操作符访问XML对象的子节点，使用@操作符访问某一节点的属性。考虑以下XML对象：
</p>
				<pre>
						<br />var x:XML =<br />      &lt;book ISBN="0942407296"&gt;<br />           &lt;title&gt;Baking Extravagant Pastries with Kumquats&lt;/title&gt;<br />           &lt;author&gt;<br />               &lt;lastName&gt;Contino&lt;/lastName&gt;<br />               &lt;firstName&gt;Chuck&lt;/firstName&gt;<br />           &lt;/author&gt;<br />           &lt;pageCount&gt;238&lt;/pageCount&gt;<br />       &lt;/book&gt;<br />       &lt;book ISBN="0865436401"&gt;<br />           &lt;title&gt;Emu Care and Breeding&lt;/title&gt;<br />           &lt;editor&gt;<br />               &lt;lastName&gt;Case&lt;/lastName&gt;<br />               &lt;firstName&gt;Justin&lt;/firstName&gt;<br />           &lt;/editor&gt;<br />           &lt;pageCount&gt;115&lt;/pageCount&gt;<br />       &lt;/book&gt;<br />   &lt;/order&gt;<br /></pre>
				<p class="zh_p">对象x.book表示一个包含所有名称为book的子节点的XMLList对象，该XMLList包含两个XML对象（两个book节点）。</p>
				<p class="zh_p">对象x..lastName表示一个包含XML树结构下部所有的lastName属性的XMLList对象，该XMList包含两个XML对象（两个LastName属性）。</p>
				<p class="zh_p">对象x.book.editor.lastName表示一个包含所有x对象的所有名称为book的子节点的所有名称为
editor的子节点的所有lastName节点的XMLList对象，该XMLList只包含一个XML对象(值为"Case"的lastName属
性)。</p>
				<p class="zh_p">
						<strong>访问父节点和子节点</strong>
				</p>
				<p class="zh_p">parent()方法返回XML对象的父节点。</p>
				<p class="zh_p">你可以使用子节点列表的顺序索引值来访问特定的子节点，例如，假定某一XML对象x有两个名称为book的子节点，你可以如下访问它们：</p>
				<pre>//第1个book节点<br />x.book[0]<br />//第2个book节点<br />x.book[1]<br /></pre>
				<p class="zh_p">要访问孙子节点，我们可以如下直接使用儿子和孙子节点的索引值来访问：</p>
				<pre>x.book[0].title[0]</pre>
				<p class="zh_p">不过如果x.book[0]只有一个名称为title的子节点的话，那么可以省略索引：</p>
				<pre>x.book[0].title</pre>
				<p class="zh_p">类似的，如果x对象只有一个book子节点，并且该book节点的子节点对象只有一个title对象，那么两个索引值都可以省略：</p>
				<p class="zh_p">x.book.title</p>
				<p class="zh_p">注意，你也可以使用child()方法，直接使用名称访问特定的子节点：</p>
				<pre>var x.XML =<br />       &lt;order&gt;<br />           &lt;book&gt;<br />               &lt;title&gt;Dictionary&lt;/title&gt;<br />           &lt;/book&gt;<br />       &lt;/order&gt;<br /><br />var childName:String = "book";<br />trace (x.child(childName).title) // Output: Dictionary<br /></pre>
				<p class="zh_p">
						<strong>访问属性</strong>
				</p>
				<p class="zh_p">我们使用使用@操作符访问XMLNode属性：</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;<br /><br />//获取第1个item的id属性值<br />trace(myXML.item[0].@id);//Output:1<br /></pre>
				<p class="zh_p">
						<strong>使用属性或元素值过滤XML数据</strong>
				</p>
				<p class="zh_p">我们可以使用特定的元素名称和属性值来定位到特定的元素考虑以下XML对象：</p>
				<pre>var x:XML =<br />   &lt;employeeList&gt;<br />       &lt;employee id="347"&gt;<br />           &lt;lastName&gt;Zmed&lt;/lastName&gt;<br />           &lt;firstName&gt;Sue&lt;/firstName&gt;<br />           &lt;position&gt;Data analyst&lt;/position&gt;<br />       &lt;/employee&gt;<br />       &lt;employee id="348"&gt;<br />           &lt;lastName&gt;McGee&lt;/lastName&gt;<br />           &lt;firstName&gt;Chuck&lt;/firstName&gt;<br />           &lt;position&gt;Jr. data analyst&lt;/position&gt;<br />       &lt;/employee&gt;<br />   &lt;/employeeList&gt;<br /><br /></pre>
				<p class="zh_p">以下是正确的访问方法：</p>
				<pre>//lastName为“McGee”的employee对象，第1个employee节点<br />x.employee.(lastName == "McGee") // The first employee node<br />//lastName为“McGee”的employee对象的firstName节点，第1个employee节点的节点<br />x.employee.(lastName == "McGee").firstName // The firstName property of that node<br />//lastName为“McGee”的id属性<br />x.employee.(lastName == "McGee").@id // The value of the id attribute<br />//所有id属性值为347的employee对象列表<br />x.employee.(@id == 347)<br />//id属性值为347的employee对象的lastName子节点<br />x.employee.(@id == 347).lastName<br />//所有id属性值大于347的employee对象列表<br />x.employee.(@id &gt; 300) // An XML list with both employee properties<br />//所有position子节点值包含“analyst”的employee对象列表<br />x.employee.(position.toString().search("analyst") &gt; -1)<br /></pre>
				<p class="zh_p">
						<strong>使用for ... in和for each ... in 语句</strong>
				</p>
				<p class="zh_p">ActionScript 3.0 包括了一个新用来遍历XMLList对象的的for ... in语句和for
each ... in语句。例如，考虑以下XML对象，myXML和myXML..item XMLList对象（包含两个item
XML对象节点的XML列表）：</p>
				<pre>var myXML:XML =<br />   &lt;order&gt;<br />       &lt;item id='1' quantity='2'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />       &lt;item id='2' quantity='2'&gt;<br />           &lt;menuName&gt;fries&lt;/menuName&gt;<br />           &lt;price&gt;1.45&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;/order&gt;<br /></pre>
				<p class="zh_p">for ... in语句可以让我们遍历XMLList的所有属性名称,实际上就是个节点的索引值：</p>
				<pre>var total:Number = 0;<br />for (var pname:String in myXML..item)<br />{<br />    total += Number(myXML.item.@quantity[pname]) * Number(myXML.item.price[pname]);<br />}<br /></pre>
				<p class="zh_p">for each ... in语句遍历XMLList的所有节点：</p>
				<pre>var total2:Number = 0;<br />for each (var item:XML in myXML..item)<br />{<br />  total2 += Number(item@quantity) * Number(item.price);<br />}<br /></pre>
				<p class="zh_p">
						<strong>使用with语句</strong>
				</p>
				<p class="zh_p">我们可以使用with语句，来指明后续的属性和节点值都是基于某一XML对象，前面的for each ... in示例代码，使用with语句的代码如下：</p>
				<pre>var total:Number = 0;<br />for each (var item in myXML..item)<br />{<br />   with (item)<br />    {<br />         //{内的属性和节点对象都是基于item XML对象的，所有不需要使用item.来访问。<br />         total += Number(@quantity) * Number(price);<br />     }<br /><br />}<br />trace(total);<br /></pre>
				<p class="zh_p">
						<strong>修改XML对象</strong>
				</p>
				<p class="zh_p">我们可以使用prependChild()方法或者appendChild()方法在XML对象的子节点列表的前面或者最后面添加节点：</p>
				<pre>var x1:XML = &lt;p&gt;Paragraph 1&lt;/p&gt;<br />var x2:XML = &lt;p&gt;Paragraph 2&lt;/p&gt;<br />var x:XML = &lt;body&gt;&lt;/body&gt;<br />x = x.appendChild(x1);<br />x = x.appendChild(x2);<br />x = x.prependChild(&lt;p&gt;Paragraph 0&lt;/p&gt;);<br /><br />// x == &lt;body&gt;&lt;p&gt;Paragraph 0&lt;/p&gt;&lt;p&gt;Paragraph 1&lt;/p&gt;&lt;p&gt;Paragraph 2&lt;/p&gt;&lt;/body&gt;<br /></pre>
				<p class="zh_p">使用insertChildBefore()方法或者insertChildAfter()方法在特定的节点前面活着回后面添加节点：</p>
				<pre>var x:XML =<br />   &lt;body&gt;<br />       &lt;p&gt;Paragraph 1&lt;/p&gt; <br />       &lt;p&gt;Paragraph 2&lt;/p&gt;<br />   &lt;/body&gt;<br /><br />var newNode:XML = &lt;p&gt;Paragraph 1.5&lt;/p&gt;<br />x = x.insertChildAfter(x.p[0], newNode)<br />x = x.insertChildBefore(x.p[2], &lt;p&gt;Paragraph 1.75&lt;/p&gt;)<br /></pre>
				<p class="zh_p">注意，我们也可以在构造XML对象的时候使用大括号（{和}）来引用变量：</p>
				<pre>var ids:Array = [121, 122, 123];<br />var names:Array = [["Murphy","Pat"],["Thibaut","Jean"], ["Smith","Vijay"]]<br />var x:XML = new XML("&lt;employeeList&gt;&lt;/employeeList&gt;");<br /><br />for (var i:int = 0; i &lt; 3; i++) {<br />          var newnode:XML = new XML(); <br />           newnode =<br />               &lt;employee id={ids[i]}&gt;<br />                   &lt;last&gt;{names[i][0]}&lt;/last&gt;<br />                   &lt;first&gt;{names[i][1]}&lt;/first&gt;<br />               &lt;/employee&gt;<br /><br />           x = x.appendChild(newnode)<br /><br />}<br /></pre>
				<p class="zh_p">我们也可以使用=操作符来给XML对象节点赋值：</p>
				<pre>var x:XML =<br />   &lt;employee&gt;<br />       &lt;lastname&gt;Smith&lt;/lastname&gt;<br />   &lt;/employee&gt;<br /><br />x.firstname = "Jean";<br />x.@id = "239";<br /></pre>
				<p class="zh_p">以上代码将把XML对象X设置成如下：</p>
				<pre>&lt;employee id="239"&gt;<br />   &lt;lastname&gt;Smith&lt;/lastname&gt;<br />   &lt;firstname&gt;Jean&lt;/firstname&gt;<br />   &lt;/employee&gt;<br /></pre>
				<p class="zh_p">我们也可以使用+和+=操作符来连结XMLList：</p>
				<pre>var x1:XML = &lt;a&gt;test1&lt;/a&gt;<br />var x2:XML = &lt;b&gt;test2&lt;/b&gt;<br />var xList:XMLList = x1 + x2;<br />xList += &lt;c&gt;test3&lt;/c&gt;<br /></pre>
				<p class="zh_p">
						<strong>
								<em>删除XML对象</em>
						</strong>
				</p>
				<p class="zh_p">E4X规范中定义有delete和deleteByIndex方法用来删除特定的XML节点，但是在当前版本的
ActionScript
3.0实现中，并没有实现这两个方法，所有我们不能直接使用这两个方法，不过我们可以使用遍历XML树，剔除特定的节点，重新构造新的XML对象的方法来
删除某一特定节点，以下我们将删除employee的lastname子节点：</p>
				<pre>private function deleteByIndex(xmlNode:XML,index:Number):XML{<br />    var newStr:String='';<br />    newStr+='&lt;'+xmlNode.localName();<br />    for each (var att:XML in xmlNode.attributes()){<br />        newStr+=' '+att.localName()+'="'+att.toString()+'"';<br />    }<br />    newStr+='&gt;';<br />    var i:Number=0;<br />    for each(var node:XML in xmlNode.children()){<br />        if(i!=index)<br />            newStr+=node.toXMLString();<br />        i++;<br />    }<br />    newStr+='&lt;/'+xmlNode.localName()+'/&gt;';<br />    return new XML(newStr);<br />}<br /><br />var myXML:XML=<br />   &lt;employee id="239"&gt;<br />           &lt;lastname&gt;Smith&lt;/lastname&gt;<br />           &lt;firstname&gt;Jean&lt;/firstname&gt;<br />           &lt;address&gt;<br />           	&lt;city&gt;shangrao&lt;/city&gt;<br />           	&lt;load&gt;daihu&lt;/load&gt;<br />           	&lt;no&gt;5&lt;/no&gt;<br />           &lt;/address&gt;<br />       &lt;/employee&gt;<br /><br />myXML=deleteByIndex(myXML,0);<br /></pre>
				<p class="zh_p">以上的deleteByIndex函数有两个参数，第1的参数是被删除节点的父节点，第2的参数是被删除节点的在父节点的子节点列表中索引值。先遍历父节点的索引属性，然后遍历其所有的子节点，跳过我们要删除的节点，然后将它们组合成新的XML对象返回。</p>
				<p class="zh_p">如果XML对象非常复杂，数据量必较大的话，以上删除节点的实现方法效率是非常好低，所有正确的选择还是使用E4X定义的删除方法，不过这个功能要等到ActionScript 3.0的下一个测试版本才能够实现。</p>
				<p class="zh_p">
						<strong>XML类型转换</strong>
				</p>
				<p class="zh_p">我们可以将XML对象和XMLList对象转换为字符串，同样的，我们也可以将字符串转换为XML对象和XMLList对象。顺便，请记住所有的XML属性值、名称和文本值都是字符串。</p>
				<p class="zh_p">
						<strong>转换XML和XMLList对象为字符串</strong>
				</p>
				<p class="zh_p">XML对象和XMLList对象都有有两个成员方法：toString()和toXMLString()方法。
toXMLString()方法返回包含所有标签、属性、命名空间声明和XML对象内容的字符串，对与复杂的XML对象（包含有子元素），
toString()方法的效果和toXMLString()方法的效果一样，但是对与简单的XML对象（只包含一个文本元素），toString()方
法只返回元素的文本内容：</p>
				<pre>var myXML:XML =<br />  &lt;order&gt;<br />       &lt;item id='1' quantity='2'&gt;<br />           &lt;menuName&gt;burger&lt;/menuName&gt;<br />           &lt;price&gt;3.95&lt;/price&gt;<br />       &lt;/item&gt;<br />   &lt;order&gt;<br /><br />trace(myXML.item<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.menuName.toXMLString())<br />    // Output: &lt;menuName&gt;burger&lt;/menuName&gt;<br />trace(myXML.item<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.menuName.toString())<br />    // Output: burger<br /></pre>
				<p class="zh_p">将文本字符串转换为XML对象</p>
				<p class="zh_p">我们可以使用new构造方法从字符串创建一个XML对象</p>
				<pre>var x:XML = new XML('&lt;a&gt;test&lt;b&gt;');</pre>
				<p class="zh_p">但是如果我们试图将一个非XML或者结构不完整的字符串转换为XML对象，那么将会报运行时错误：</p>
				<pre>var x:XML = new XML('&lt;a&gt;test'); // Throws an error</pre>
				<p class="zh_p">
						<strong>从Internet读取Rss Fead数据</strong>
				</p>
				<p class="zh_p">以下代码将读取本站的rss fead数据：</p>
				<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />  &lt;mx:Application xmlns:mx="http://www.macromedia.com/2005/mxml" xmlns="*" creationComplete="doInit()"&gt;<br />  &lt;mx:Script&gt;<br />  &lt;![CDATA[<br />    private function doInit():Void{<br />      getRssData("http://blog.eshangrao.com/rss.php",ta_view);<br />    }<br /><br />    public function getRssData(url:String, outTxt:TextArea):Void<br />    {<br />      private var myXMLURL:URLRequest = new URLRequest(url);<br />      private var myLoader:URLLoader = new URLLoader(myXMLURL);<br />      myLoader.addEventListener("complete", xmlLoaded);<br />    }<br />    private function xmlLoaded(event:Event):Void{<br />      ta_view.text='load ok';<br />      var myLoader:URLLoader = URLLoader(event.target);<br />      XML.ignoreProcessingInstructions=false;<br />      var myXML:XML =new XML(myLoader.data);<br />      private var outStr:String = "";<br />      for each (var item:XML in myXML.children()) {<br />        if(item.localName()=='item'){<br />          outStr += "&lt;p&gt;&lt;b&gt;" + item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/0">0</a>.toString() + ":&lt;/b&gt;&lt;/p&gt;&lt;p&gt;";<br />          outStr += item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/6">6</a>.toString() ;<br />          outStr += " &lt;br/&gt;&lt;a href='" + item.children()<a href="http://www.flexlib.cn/index.php/2006/01/04/1">1</a>.toString();<br />          outStr += "'&gt;&lt;font color='#008000'&gt;More...&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;";<br />        }<br />      }<br />      //ta_view.text=myXML.toString();<br />      ta_view.htmlText = outStr;<br />    }<br />  ]]&gt;<br />  &lt;/mx:Script&gt;<br />  &lt;mx:Canvas width="100%" height="100%"&gt;<br />    &lt;mx:TextArea id="ta_view"&gt;<br />      &lt;mx:layoutConstraints&gt;<br />        &lt;mx:EdgeAnchor bottom="10" top="10" left="10" right="10"/&gt;<br />      &lt;/mx:layoutConstraints&gt;<br />    &lt;/mx:TextArea&gt;<br />  &lt;/mx:Canvas&gt;<br />&lt;/mx:Application&gt;<br /></pre>
				<p class="zh_p">
						<a href="http://labs.eshangrao.com/flex/2/RssReaderExample.html" target="_blank">运行示例</a>(FlashPlayer8.5 required)</p>
				<p class="zh_p">注意，我们没有直接使用节点名称访问节点（不知道为什么，如果使用item.title访问title节点的话，返回是
空的，可能跟我的RSS
XML的中rdf指令有关，如果有朋友知道解决的办法，请告诉我），而是使用了children()方法，该方法返回某一XML对象的所有子节点对象，</p>
				<p class="zh_p">获取更多AS3中E4X类的使用信息，请查看<a href="http://livedocs.macromedia.com/labs/1/flex/00002041.html" hreflang="en">Adobe在线文档</a>，详细API参考：<a href="http://livedocs.macromedia.com/labs/1/flex/langref/index.html">Adobe Flex 2 API文档</a></p>
		</div>
<img src ="http://www.blogjava.net/mstar/aggbug/121136.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2007-05-31 13:28 <a href="http://www.blogjava.net/mstar/archive/2007/05/31/121136.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]动态更改页面的CSS</title><link>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 14 Jun 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/52686.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/06/14/52686.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/52686.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/52686.html</trackback:ping><description><![CDATA[
		<h1 class="title">
				<a href="http://www.alistapart.com/articles/alternate">Alternative Style: Working With Alternate Style Sheets</a>
		</h1>
		<div id="articletext">
				<p>
						<br />
						<br />So you've got a web page. You’ve marked it up with structural XHTML. You’ve also been a good little web developer and used style sheets to control what your document looks like. You’ve even gone the extra mile and created several alternative style sheets to show how hardcore you are.</p>
				<p>Great. But now you need a cross-browser way to dynamically switch between the style sheets.</p>
				<h2>Styling your site</h2>
				<p>Style sheets can be associated with documents using a list of link elements in the head. There are three different relationships external style sheets can have with the document: persistent, preferred, and alternate.</p>
				<h3>Persistent</h3>
				<p>These style sheets are always enabled (they are always “on”) and are combined with the active style sheet. They can be used for shared rules common to every style sheet. To make a style sheet persistent, the rel attribute is set to “stylesheet” and no title attribute is set.</p>
				<p>To make the style sheet <code>paul.css</code> persistent, the following link element would be included in the head:</p>
				<pre>&lt;link rel="stylesheet" 
type="text/css" href="paul.css" /&gt;
</pre>
				<h3>Preferred</h3>
				<p>These style sheets are enabled by default (they are “on” when the page is loaded). They can then be disabled if the user selects an alternate style sheet.</p>
				<p>To make a style sheet preferred, the <code>rel</code> attribute is set to “stylesheet” and the style sheet is named with the <code>title</code> attribute. </p>
				<p>Several preferred style sheets can be grouped together by giving them identical title attributes. These grouped style sheets are then all enabled and disabled together. If more than one group of preferred style sheets are declared, the first group takes precedence.</p>
				<p>To make <code>paul.css</code> preferred, a title attribute is added, giving the default style a name.</p>
				<pre>&lt;link rel="stylesheet" 
type="text/css" href="paul.css" 
title="bog standard" /&gt;
</pre>
				<h3>Alternate</h3>
				<p>These style sheets can be selected by the visitor as alternatives to the preferred style sheet. This allows the visitor to personalize a site and choose his or her favorite scheme. They can also be used for accessibility.</p>
				<p>To specify an alternate style sheet, the rel attribute is set to “alternate stylesheet” and the style sheet is named with a title attribute. As with preferred sheets, these style sheets can also be grouped together by giving them identical title attributes.</p>
				<p>Using the previous example again; to make <code>paul.css</code> into an alternate style sheet, the keyword “alternate” is added to the rel attribute.</p>
				<pre>&lt;link rel="alternate stylesheet" 
type="text/css" href="paul.css"
title="wacky" /&gt;
</pre>
				<p>Note that these relationships only apply to external style sheets which are included using the link element.</p>
				<h2>Swappin’ Styles</h2>
				<p>When a document is initially loaded, the persistent and preferred style sheets are applied to the document. The alternate style sheets can then be selected by the user. The W3C tells us that the browser should give us a choice of the style sheet we want to use, and suggests that perhaps a drop–down menu or tool bar will be provided.</p>
				<p>So far, so good. We have several style sheets and the visitor can choose their favorite from a menu. But then we encounter a problem. A major one. Mozilla provides a menu to select the style sheet we want to use under the view menu item. But Microsoft Internet Explorer (MSIE) provides no such menu. So we have several style sheets, and no way to access them in MSIE.</p>
				<p>Here’s where a little bit of JavaScript can be used along with the DOM to provide a way for MSIE and Mozilla users to select the style sheet they want to use. Their preference can also be stored in a cookie. And because we are using the link tags as the W3C tells us to, the JavaScript doesn’t interfere with the menu in Mozilla, and it degrades very gracefully.</p>
				<h2>The Script</h2>
				<p>First we need the script to be able to differentiate between the three different types of style sheet. This is relatively easy to do, as we only need to check two of the attributes of each link element.</p>
				<p>Is it a link to a style sheet?</p>
				<pre>HTMLLinkElement.getAttribute("rel").indexOf("style") != -1
</pre>
				<p>Is there a title attribute?</p>
				<pre>HTMLListElement.getAttribute("title")
</pre>
				<p>Does the rel attribute contain the keyword "alternate"?</p>
				<pre>HTMLLinkElement.getAttribute("rel").indexOf("alt") != -1
</pre>
				<p>Note that we check for the string “alt” because some browsers accept the keyword “alternative” in place of “alternate.”</p>
				<p>Using these three checks we can write a function to switch style sheets. This involves looping through every link element in the document, disabling all preferred and alternate style sheets that we <em>don’t</em> want active, and enabling all preferred and alternate style sheets that we <em>do</em> want active.</p>
				<p>Note that only preferred and alternate style sheet link elements will have a title attribute.</p>
				<p>The change function looks like this:</p>
				<pre> 
function setActiveStyleSheet(title) {
   var i, a, main;
   for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
     if(a.getAttribute("rel").indexOf("style") != -1
        &amp;&amp; a.getAttribute("title")) {
       a.disabled = true;
       if(a.getAttribute("title") == title) a.disabled = false;
     }
   }
}
</pre>
				<h3>Cookies</h3>
				<p>Now we can change the style sheet. Cool. We have a more personalized page. Excellent. But we don’t have a personalized site. The preference is only applied to the current page; when we leave the current page the preference leaves with us. This situation, however, can be rectified with a cookie.</p>
				<p>To store a cookie we need another function to return the current style sheet. We also need two functions to store and read the cookie.</p>
				<p>To return the current style sheet we look for an active preferred or alternate style sheet and check its title.</p>
				<p>First we loop through all the link elements in the document again. We then check whether the link is a style sheet. If it is, we check whether the style sheet has a title. This tells us that the style sheet is either preferred or alternative.</p>
				<p>The last check is to see whether or not the style sheet is active. If all three checks return true, we have the current style sheet and we can return the title.</p>
				<p>The function ends up looking like this:</p>
				<pre> 
function getActiveStyleSheet() {
var i, a;
 for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
  if(a.getAttribute("rel").indexOf("style") != -1
  &amp;&amp; a.getAttribute("title")
  &amp;&amp; !a.disabled) return a.getAttribute("title");
  }
  return null;
}
</pre>
				<p>As this is an article on style, and cookies are a completely different topic, I won’t explain the cookie functions here, but I will include them for your convenience (these <a href="http://www.xs4all.nl/%7Eppk/js/cookies.html">functions</a> are written by ALA author <a href="http://www.xs4all.nl/%7Eppk/js/index.html?/%7Eppk/js/intro.html">Peter-Paul Koch</a>).</p>
				<pre> 
  function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i &lt; ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}
</pre>
				<p>To use these cookie functions, we need to add <code>onload</code> and <code>onunload</code><a href="http://www.alistapart.com/articles/domtricks2/">event listeners</a> to the window.</p>
				<h3>onLoad</h3>
				<p>There is a w3c specified DOM Level 2 attribute, “disabled,” that is set to false when a style sheet is applied to the document. This attribute is correctly implemented in Mozilla, but unfortunately not in MSIE.</p>
				<p>MSIE <em>does</em> have a proprietary HTML attribute, also called “disabled,” that applies to link elements. This attribute is initially set to false for all link elements.</p>
				<p>To set the MSIE disabled attribute to match the DOM Level 2 disabled attribute, we can call the setActiveStyleSheet() function with the name of the preferred style sheet.</p>
				<p>To find out which style sheet is the preferred style sheet, we need another function. Because this function is so similar to the <code>getActiveStyleSheet()</code> function I’m not going to explain how it works, but here is what it may look like:</p>
				<pre> 
  function getPreferredStyleSheet() {
  var i, a;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1
       &amp;&amp; a.getAttribute("rel").indexOf("alt") == -1
       &amp;&amp; a.getAttribute("title")
       ) return a.getAttribute("title");
  }
  return null;
}
</pre>
				<p>In the onload function, we first set a title variable. This either holds the value of the previous style sheet that is stored in the cookie, or if there isn’t one, the title of our preferred style sheet. To keep things logical, let’s call the cookie “style.”</p>
				<p>Next we call up the <code>setActiveStyleSheet()</code> function passing the title variable as the title. Our onload function looks something like this:</p>
				<pre> 
  window.onload = function(e) {
  var cookie = readCookie("style");
  var title = cookie ? cookie : getPreferredStyleSheet();
  setActiveStyleSheet(title);
}</pre>
				<p>Note that it may be desirable to call this function before the onload event as well, causing the document to “paint” with our style sheet preference.</p>
				<p>If you choose to do this, make sure the function is called after the functions and the link elements have been defined.</p>
				<h3>onUnload</h3>
				<p>To save the cookie in the onunload event is simpler. All we have to do is use the <code>getActiveStyleSheet()</code> function to return the active style sheet, and save this in a cookie. Using the function to store a cookie we will end up with something like this:</p>
				<pre> 
  window.onunload = function(e) {
  var title = getActiveStyleSheet();
  createCookie("style", title, 365);
}
</pre>
				<h2>Puttin’ it all together</h2>
				<p>To use these functions to make your website more sexy, you need to include them in your document. To make it easy, I have put them all together in a javascript file, ready for you to <a href="http://www.alistapart.com/d/alternate/styleswitcher.js">download</a> and add to your site.</p>
				<h4>
						<a href="http://www.alistapart.com/d/alternate/styleswitcher.js">Download styleswitcher.js</a>
				</h4>
				<p>To include the javascript file, you add a script element to the head of your document, making sure that it is put below all the style sheet link elements you have. The HTML would look like this:</p>
				<pre>&lt;script type="text/javascript" 
src="/scripts/styleswitcher.js"&gt;&lt;/script&gt;
</pre>
				<p>To allow the visitor to change the active style sheet, you could use javascript onClick events. For example, to have the option to switch between two themes with titles “default” and “paul,” you could use the following HTML:</p>
				<pre>&lt;a href="#" 
onclick="setActiveStyleSheet('default'); 
return false;"&gt;change style to default&lt;/a&gt;

&lt;a href="#" 
onclick="setActiveStyleSheet('paul'); 
return false;"&gt;change style to paul&lt;/a&gt;
</pre>
				<p class="vs10">Once the visitor has selected a theme, it will be stored in a cookie. To use the same theme throughout your website, the same style sheet and javascript link elements should be included in the head of every page of the site.</p>
				<h3>That’s all, folks!</h3>
				<p>There you have it, a customizable website that uses link elements to link to style sheets as the W3C has told us we should. Enjoy!</p>
		</div>
<img src ="http://www.blogjava.net/mstar/aggbug/52686.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-06-14 11:45 <a href="http://www.blogjava.net/mstar/archive/2006/06/14/52686.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java &amp; Rhino &amp; Groovy 性能啊！！</title><link>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 07 Jun 2006 07:44:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/51145.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/06/07/51145.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/51145.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/51145.html</trackback:ping><description><![CDATA[早就听说Groovy性能比Java还要差很多，于是一直没有花心是去研究它，今天在newsgroup中有看到了有人抱怨Groovy的性能。于是自己也做了个测试，同时也把Rhino拉了进来。<br /><br />Rhino是Mozilla的javascript脚本引擎，和Groovy性质差不多，但是没有Groovy功能那么多。<br /><br />代码如下<br />BenchMark.java<br /><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);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> BenchMark{<br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> main(String args[]){<br />          </span><span style="color: rgb(0, 0, 255);">long</span><span style="color: rgb(0, 0, 0);"> t</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br />          </span><span style="color: rgb(0, 0, 255);">double</span><span style="color: rgb(0, 0, 0);"> seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);">;<br />          </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> j</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />              </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />              {<br />                  seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">Math.cos(seed</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);<br />              }<br />          </span><span style="color: rgb(0, 0, 255);">long</span><span style="color: rgb(0, 0, 0);"> t2</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br />          System.out.println(t2</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">t);<br />    }<br />}</span></div><br />BenchMark.js<br /><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, 0);">importPackage(java.lang);<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> t </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> System.currentTimeMillis();<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> seed </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);">;<br /></span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> j </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">)<br />    </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> i</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">; i</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">){<br />      seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">Math.cos(seed</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);<br />    }<br /></span><span style="color: rgb(0, 0, 255);">var</span><span style="color: rgb(0, 0, 0);"> t2 </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> System.currentTimeMillis();<br />print(t2</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">t);</span></div><br />BenchMark.groovy<br /><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);">long</span><span style="color: rgb(0, 0, 0);"> t</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">System.currentTimeMillis();<br /></span><span style="color: rgb(0, 0, 255);">double</span><span style="color: rgb(0, 0, 0);"> seed</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">0.5</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">.times{<br /> </span><span style="color: rgb(0, 0, 0);">1000000</span><span style="color: rgb(0, 0, 0);">.times{i </span><span style="color: rgb(0, 0, 0);">-&gt;</span><span style="color: rgb(0, 0, 0);"> seed </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Math.cos(seed </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);"> i)}<br />}<br />println System.currentTimeMillis() </span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);"> t</span></div><br />我的机器<br />P4 3G 超线程<br />内存 1G（这个测试好像和内存没什么关系）<br /><br />java代码当然需要编译才能运行的，js和groovy是直接解释脚本运行，结果如下<br />Java 3813<br />Rhino 34015<br />Groovy 103422（不知道为什么我这个数值似乎有点比别人的大）<br />然后我把js和groovy编译成class，结果差不多，（其实我的结果是编译后的groovy和js都要比不编译还要慢），所以把它们编译了意义应该不大。<br /><br />虽然我这个测试不具有什么现实意义，但是我还是感觉用Groovy（Grails）去做网站不大可行。倒是可以考虑把它们用在不经常被调用，但是代码却要经常变的地方。<br /><img src ="http://www.blogjava.net/mstar/aggbug/51145.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-06-07 15:44 <a href="http://www.blogjava.net/mstar/archive/2006/06/07/51145.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]由String类型字符组成的日期的星期</title><link>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 11 Apr 2006 02:14:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/40385.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/04/11/40385.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/40385.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/40385.html</trackback:ping><description><![CDATA[
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">html</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">head</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">title</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">测试网页</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">title</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">head</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">body</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;</span>
				<span style="COLOR: #800000">script</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">
						<br />
				</span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myYr</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">2006</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myMonth</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">4</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> myDate</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">11</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">;<br /></span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">var</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> strWeek</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">星期</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">+</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">日一二三四五六</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">.charAt(</span>
				<span style="COLOR: #0000ff; BACKGROUND-COLOR: #f5f5f5">new</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> Date(myYr,myMonth</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">-</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">1</span>
				<span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">,myDate).getDay());<br />alert(strWeek);<br /></span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">script</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">body</span>
				<span style="COLOR: #0000ff">&gt;</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">&lt;/</span>
				<span style="COLOR: #800000">html</span>
				<span style="COLOR: #0000ff">&gt;</span>
		</div>
		<br />
		<br />我们的4月就是老外的3月啊，所以要想获得4月的月份，就先要减去1<img src ="http://www.blogjava.net/mstar/aggbug/40385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-04-11 10:14 <a href="http://www.blogjava.net/mstar/archive/2006/04/11/40385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[备份]限制用户只能在input里输入中文</title><link>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 22 Mar 2006 11:14:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/36902.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2006/03/22/36902.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/36902.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/36902.html</trackback:ping><description><![CDATA[
		<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);">
				<font size="4">
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);">script language</span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);">javascript</span>
						<span style="color: rgb(0, 0, 0);">&gt;</span>
				</font>
				<span style="color: rgb(0, 0, 0);">
						<br />
				</span>
				<font size="4">
						<span style="color: rgb(0, 0, 255);">function</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> isChineseChar(strInput){<br />    </span>
						<span style="color: rgb(0, 0, 255);">var</span>
						<span style="color: rgb(0, 0, 0);"> len </span>
						<span style="color: rgb(0, 0, 0);">=</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> strInput.length;<br />    </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);">(</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">==</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);"> len)<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">false</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />    </span>
						<span style="color: rgb(0, 0, 255);">for</span>
						<span style="color: rgb(0, 0, 0);">(</span>
						<span style="color: rgb(0, 0, 255);">var</span>
						<span style="color: rgb(0, 0, 0);"> i</span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">;i</span>
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);">len;i</span>
						<span style="color: rgb(0, 0, 0);">++</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">){<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> (strInput.charCodeAt(i)</span>
						<span style="color: rgb(0, 0, 0);">&lt;=</span>
						<span style="color: rgb(0, 0, 0);">255</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">)<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">false</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />    }<br />    </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">true</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">;<br />}<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">skdj</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">点算法</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br />alert(isChineseChar(</span>
						<span style="color: rgb(0, 0, 0);">"</span>
						<span style="color: rgb(0, 0, 0);">点算法sdf</span>
						<span style="color: rgb(0, 0, 0);">"</span>
				</font>
				<font size="4">
						<span style="color: rgb(0, 0, 0);">));<br /></span>
						<span style="color: rgb(0, 0, 0);">&lt;/</span>
						<span style="color: rgb(0, 0, 0);">script</span>
						<span style="color: rgb(0, 0, 0);">&gt;</span>
				</font>
		</div>
		<br />正则表达式<br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><font size="4"><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">input type</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">text</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> onkeyup</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">value=value.replace(/[^\u4E00-\u9FA5]/g,'')</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> onbeforepaste</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">clipboardData.setData('text', clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">&gt;</span></font><span style="color: rgb(0, 0, 0);">      </span></div><img src ="http://www.blogjava.net/mstar/aggbug/36902.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2006-03-22 19:14 <a href="http://www.blogjava.net/mstar/archive/2006/03/22/36902.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 用 Groovy 打造服务器端</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6263.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6263.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6263.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6263.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE><ABSTRACT-EXTENDED>Groovlet 和 GroovyServer Pages（GSP）框架都是建立在 Java Servlet API 基础之上。不过，与 Strut 和 JSF 不同，Groovy 的服务器端实现不意味着适用于所有情况。相反，它提供了一种快速而又方便地开发服务器端应用程序的简化方法。下面请跟随 Groovy 的鼓吹者 Andrew Glover，听听他如何介绍这些框架，并展示它们的应用。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>Java 平台为自己赢得了服务器端应用程序开发的首选平台的名声。Servlet 是服务器端 Java 技术的强大支柱，因此有无数的框架是围绕着 Servlet API 建立起来的，其中包括 Strut、JavaServer Faces (JSF) 和 Tapestry。您可能已经猜到，Groovy 也是以 Servlet API 为基础建立起来的框架，不过，这个框架的目的是简化开发。</P>
<P>Groovlet 和 GroovyServer Pages（GSP）框架的目的是提供一种优雅而又简单的平台，将它用于构建复杂程度不高的 Web 应用程序。就像 GroovySql 不是数据库开发的惟一选择一样，Groovlet 框架也<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不是</I> 像 Strut 那样具有更丰富功能的框架的替代品。Groovlet 只是开发人员寻求容易配置和产生工作代码的快速方法时的一种选择。</P>
<P>例如，不久前，我需要 —— <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">快速地</I> —— 提供一个 stub 应用程序，以测试像 <CODE>xml-rpc</CODE> API 这样的客户端。显然可以用一个 servlet 快速编写出所需要的功能，但是我从没想过钻研 Strut，一秒钟也没有。我考虑过使用基本的普通 Java Servlet API 编写 servlet 及其相关的逻辑，但是由于需要尽快地使用这项功能，所以我选择了使用 Groovlet 快速完成它。</P>
<P>很快您就可看到，这种选择是显而易见的。</P>
<P>在深入研究使用 Groovlet 进行编程之前，我想简单介绍一个在示例代码中会用到的 Groovy 特性。在几个月前的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-alj08034/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre: 感受 Groovy</A> 一文中，我第一次介绍了 <CODE>def</CODE> 关键字。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10071><B>关于本系列</B></A><BR>将任何工具添加到开发实践中的关键是了解什么时候使用它和什么时候不使用它。脚本语言可以为您增加特别强大的工具，但前提是在相关的场景中正确地使用它。因此，<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 的系列文章专门探讨 Groovy 的实际使用，并指导读者什么时候以及如何成功地使用这些工具。</P></TD></TR></TBODY></TABLE></P>
<P><A name=N1007E><SPAN class=atitle2>在脚本中定义函数</SPAN></A><BR>在普通 Java 编程中，方法必须存在于类对象中。事实上，所有行为都必须在类的上下文中定义。不过在 Groovy 中，行为可以在<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">函数</I> 中定义，而函数可以在类定义之外定义。</P>
<P>这些函数可以直接用名称引用，并且可以在 Groovy 脚本中定义，这样非常有助于它们的重复使用。Groovy 函数需要 <CODE>def</CODE> 关键字，可以将关键字想像为在脚本范围内可用的全局静态方法。因为 Groovy 是动态类型的语言，所以 <CODE>def</CODE> 不需要对参数作任何类型声明，<CODE>def</CODE> 也不需要 <CODE>return</CODE> 语句。</P>
<P>例如，在清单 1 中，我定义了一个简单的函数，它将输出一个集合的内容，而不管这个集合是 <CODE>list</CODE> 还是 <CODE>map</CODE>。然后我定义一个 <CODE>list</CODE>，填充它，并调用我新定义的 <CODE>def</CODE>。之后，我创建一个 <CODE>map</CODE>，并对这个集合做了同样的操作。</P><A name=code1><B>清单 1. 这就是 def!</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
def logCollection(coll){
  counter = 0;
  coll.each{ x | 
    println "${++counter} item: ${x}"
  }
}
lst = [12, 3, "Andy", 'c']
logCollection(lst)
mp = ["name" : "Groovy", "date" : new Date()]
logCollection(mp)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>def</CODE> 不需要 <CODE>return</CODE> 语句，因此如果最后一行产生某个值，那么这个值由 <CODE>def</CODE> 返回。例如，在清单 2 中，代码定义了一个 <CODE>def</CODE>，它返回传递进来的变量的名称。我可以编写它，让它带有或者不带 <CODE>return</CODE> 语句，得到的结果是相同的。</P><A name=code2><B>清单 2. 在 def 中 return 语句是可选的</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
def getJavaType(val){
  val.class.getName()
}
tst = "Test"
println getJavaType(tst)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在编写简单的脚本时，<CODE>def</CODE> 关键字会非常好用。您很快就会看到，在开发 Groovlet 时，这个关键字也会派上用场。</P>
<P><A name=N100E7><SPAN class=atitle2>Groovlet 和 GSP</SPAN></A><BR>使用 Groovlet 和 GSP 的前提条件相当简单：需要一个 servlet 容器，以及最新、最伟大版本的 Groovy。这些框架的好处是它们通过一个 web.xml 文件将所选模式的所有 URL 映射到特定的 servlet。因此，建立 Groovlet 和 GSP 的实现的第一步是定义一个 Web 应用程序上下文，并更新它的相关 web.xml 文件。这个文件将包括特定的 servlet 类定义以及它们对应的 URL 模式。</P>
<P>我将使用 Apache Jakarta Tomcat，并且已创建了一个名为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groove</I> 的上下文。目录结构如清单 3 所示：</P><A name=code3><B>清单 3. groove 上下文的目录列表 </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
./groove:
drwxrwxrwx+   3 aglover  users        0 Jan 19 12:14 WEB-INF
./WEB-INF:
-rwxrwxrwx+   1 aglover  users      906 Jan 16 14:37 web.xml
drwxrwxrwx+   2 aglover  users        0 Jan 19 17:12 lib
./WEB-INF/lib:
-rwxrwxrwx+   1 aglover  users   832173 Jan 16 14:28 groovy-1.0-beta-9.jar
-rwxrwxrwx+   1 aglover  users    26337 Jan 16 14:29 asm-1.5.2.jar
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在 WEB-INF 目录中要有一个 web.xml 文件，它至少有一个清单 4 中的元素：</P><A name=code4><B>清单 4. 一个完全配置的 web.xml 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
    "http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4"&gt;
    &lt;servlet&gt;
      &lt;servlet-name&gt;GroovyServlet&lt;/servlet-name&gt;
      &lt;servlet-class&gt;groovy.servlet.GroovyServlet&lt;/servlet-class&gt;
    &lt;/servlet&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;GroovyTemplate&lt;/servlet-name&gt;
        &lt;servlet-class&gt;groovy.servlet.TemplateServlet&lt;/servlet-class&gt;    
	&lt;/servlet&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;GroovyServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;*.groovy&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;GroovyTemplate&lt;/servlet-name&gt;
        &lt;url-pattern&gt;*.gsp&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上述 web.xml 文件中的定义声明了以下内容：所有以 <CODE>.groovy</CODE> 结尾的请求（如 <CODE>http://localhost:8080/groove/hello.groovy</CODE>）都将发送给类 <CODE>groovy.servlet.GroovyServlet</CODE>，而所有以 <CODE>.gsp</CODE> 结尾的请求都将送给类 <CODE>groovy.servlet.TemplateServlet</CODE>。</P>
<P>下一步是将两个 jar 放到 lib 目录中：groovy 发行版本的 jar（在这里是 groovy-1.0-beta-9.jar）和对应的 <CODE>asm</CODE> jar （对于 groovy beta-9 来说是 asm-1.5.2.jar）。</P>
<P>瞧，就是这么简单 —— 我已经准备好了。</P>
<P><A name=N10130><SPAN class=atitle2>Groovlet，请出场</SPAN></A><BR>编写 Groovlet 无疑很简单，因为 Groovy 只有很少的几个对类继承扩展的要求。使用 Groovlet 不需要扩展 <CODE>javax.servlet.http.HttpServlet</CODE>、<CODE>javax.servlet.GenericServlet</CODE> 或者一些华而不实的 <CODE>GroovyServlet</CODE> 类。事实上，创建 Groovlet 就像创建一个 Groovy 脚本一样简单。甚至不必创建一个类。在清单 5 中，我编写了一个简单的 Groovlet，它做两件事：打印一些 HTML，然后提供一些关于它所在的容器的信息。</P><A name=code5><B>清单 5. 开始使用 Groovlet</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
println """
&lt;html&gt;&lt;head&gt;
&lt;title&gt;Groovlets 101&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;
Welcome to Groovlets 101. As you can see
this Groovlet is fairly simple.
&lt;/p&gt;
&lt;p&gt;
This course is being run on the following servlet container: &lt;/br&gt;
${application.getServerInfo()}
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
"""
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果在浏览器中观看这个 Groovy，它看起来与图 1 所示类似。</P>
<P><A name=fig1><B>图 1. 简单 Groovlet 的输出</B></A><BR><IMG height=347 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure1.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>仔细观察清单 5 中的 Groovlet，会让您回想起第一次编写 Groovy 脚本的时候。首先，没有 <CODE>main</CODE> 方法或者类定义，只有一些简单的代码。而且，Groovlet 框架隐式地提供实例变量，比如 <CODE>ServletRequest</CODE>、<CODE>ServletResponse</CODE>、<CODE>ServletContext</CODE> 和 <CODE>HttpSession</CODE>。注意我是如何通过 <CODE>application</CODE> 变量引用 <CODE>ServletContext</CODE> 的实例的。如果想获得 <CODE>HttpSession</CODE> 的实例，那么就要使用 <CODE>session</CODE> 变量名。与此类似，可以对 <CODE>ServletRequest</CODE> 和 <CODE>ServletResponse</CODE> 分别使用 <CODE>request</CODE> 和 <CODE>response</CODE>。</P>
<P><A name=N1019C><SPAN class=atitle2>一个诊断 Groovlet</SPAN></A><BR>编写 Groovlet 不仅像创建一个 Groovy 脚本那样简单，而且还可以用 <CODE>def</CODE> 关键字定义函数，并在 Groovlet 中直接调用它们。为了展示这一点，我将创建一个非凡的 Groovlet，它将对 Web 应用程序进行一些诊断。</P>
<P>假设您编写了一个 Web 应用程序，它被世界上不同的客户所购买。您有一个大客户群，并且不断发布这个应用程序有一段时间了。从过去的支持问题中，您注意到许多急切的客户电话都与错误的 JVM 版本和错误的对象关系映射（ORM）所导致的问题有关。</P>
<P>您很忙，所以让我拿出一个解决方案。我用 Groovlet <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">迅速地</I> 创建了一个简单的诊断脚本，它将验证 VM 版本，并试图创建一个 Hibernate 会话（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。我首先创建两个函数，并在浏览器连接脚本时调用它们。清单 6 定义了这个诊断 Groovlet：</P><A name=code6><B>清单 6. 一个诊断 Groovlet</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import com.vanward.resource.hibernate.factory.DefaultHibernateSessionFactory
/**
 * Tests VM version from environment- note, even 1.5 will
 * cause an assertion error.
 */
def testVMVersion(){
  println "&lt;h3&gt;JVM Version Check: &lt;/h3&gt;"
  vers = System.getProperty("java.version")
  assert vers.startsWith("1.4"): "JVM must be at least 1.4"
  println "&lt;p&gt;JVM version: ${vers} &lt;/p&gt;"
}
/**
 * Attempts to create an instance of a hibernate session. If this
 * works we have a connection to a database; additionally, we 
 * have a properly configured hibernate instance.
 */
def testHibernate(){
  println "&lt;h3&gt;Hibernate Configuration Check: &lt;/h3&gt;"
  try{
    sessFactory = DefaultHibernateSessionFactory.getInstance()
    session = sessFactory.getHibernateSession()
    assert session != null: "Unable to create hibernate session. 
    Session was null"
    println "&lt;p&gt;Hibernate configuration check was successful&lt;/p&gt;"
  }catch(Throwable tr){
    println """
    &lt;p&gt;Unable to create hibernate session. Exception type is: &lt;br/&gt;
    &lt;i&gt;${tr.toString()} &lt;/i&gt;&lt;br/&gt;		
    &lt;/p&gt;
    """
  }   
}
println """
&lt;html&gt;&lt;head&gt;
&lt;title&gt;Diagnostics Check&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
"""
testVMVersion()
testHibernate()
println """
&lt;/body&gt;&lt;/html&gt;
"""
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这个 Groovlet 的验证逻辑非常简单，但是它可以完成这项工作。只要将诊断脚本绑定到 web 应用程序即可，当客户服务台收到电话时，它们将指点客户用浏览器访问 <CODE>Diagnostics.groovy</CODE> 脚本，并让这些脚本报告它们的发现。结果可能看起来像图 2 这样。</P>
<P><A name=fig2><B>图 2. 诊断 Groovlet 的输出</B></A><BR><IMG height=346 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure2.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N101DA><SPAN class=atitle2>那些 GSP 呢？</SPAN></A><BR>到目前为止，我主要关注于编写 Groovlet。不过，正如您将会看到的那样，很容易用 Groovy 的 GSP 页对 Groovlets 框架进行补充，就像 JSP 补充 Servlet API 一样。</P>
<P>表面上，GSP 看起来很像 JSP，实际上它们不可能有太大的差别，因为 GSP 框架其实就是一个模板引擎。如果不熟悉模板引擎，那么可能需要快速地回顾一下<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg02155/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上月的文章</A>。</P>
<P>虽然 GSP 和 JSP 是根本不同的技术，但是 GSP 是加入表达 Web 应用程序的视图的很好候选人，这一点它们是类似的。您可能会想起来，在上月的文章中，有一项促进视图的技术可以将应用程序的业务逻辑问题与其相应的视图分离。如果快速查看一下<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/index.html#code6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 6</A> 中的诊断 Groovlet，就可以看出 GSP 代码改进了哪些地方。</P>
<P>是的，Groovlet 有些简陋，不是吗？问题在于它混合了应用程序逻辑和大量输出 HTML 的 <CODE>println</CODE>。幸运的是，可以通过创建一个简单的 GSP 来补充这个 Groovlet，从而解决这个问题。</P>
<P><A name=N101F8><SPAN class=atitle3>示例 GSP</SPAN></A><BR>创建 GSP 与创建 Groovlet 一样容易。GSP 开发的关键是认识到 GSP 实质上是一个模板，因此，它最适合有限的逻辑。我将在清单 7 中创建一个简单的 GSP 作为开始：</P><A name=code7><B>清单 7. 一个简单的 GSP</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;html&gt;
&lt;head&gt;&lt;title&gt;index.gsp&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;b&gt;&lt;% println "hello gsp" %&gt;&lt;/b&gt;
&lt;p&gt;
&lt;% wrd = "Groovy"
   for (i in wrd){ 
 %&gt;
 &lt;h1&gt; &lt;%=i%&gt; &lt;br/&gt;
   
 &lt;%} %&gt;
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>观察上面的 GSP 可能很容易让您回想起标准 Groovy 模板开发。像 JSP 一样，它使用 <CODE>&lt;%</CODE>， 但是，与 Groovlet 框架类似，它允许您访问常用 servlet 对象，比如 <CODE>ServletRequest</CODE>、<CODE>ServletResponse</CODE>、<CODE>ServletContext</CODE> 和 <CODE>HttpSession</CODE> 对象。</P>
<P><A name=N10223><SPAN class=atitle2>重构应用程序 ...</SPAN></A><BR>在练习编程语言或者平台发展的时候，重构老的代码可以学到很多东西。我将重构<A href="http://www-128.ibm.com/developerworks/cn/java/j-pg01115.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">一月份专栏</A>中的简单报告应用程序，那时候您才刚开始学习 GroovySql。</P>
<P>您还记得吗，我构建了一个快速但不完善的报告应用程序，它可以在组织中有多次使用。但结果是，它变成了研究公司数据库活动的相当流行的应用程序。现在，非技术人员希望可以访问这个巨大的报告，但是他们不想很费事地在自已的计算机上安装 Groovy 来运行它。</P>
<P>我多少预计到了这种情况的发生，解决方案<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实际上</I> 是显而易见的：让报告应用程序支持 Web。很幸运，Groovlet 和 GSP 使重构变成小事一桩。</P>
<P><A name=N10239><SPAN class=atitle3>重构报告应用程序</SPAN></A><BR>首先，我将处理 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg01115.html#%20code12" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">&gt; GroovySql 一文的清单 12</A> 中的简单应用程序。重构这个应用程序很容易：只要将所有 <CODE>println</CODE> 替换成用 <CODE>setAttribute()</CODE> 方法，然后将实例变量放入 <CODE>HttpRequest</CODE> 对象的逻辑中即可。</P>
<P>下一步是用 <CODE>RequestDispatcher</CODE> 将 <CODE>request</CODE> 转发给 GSP，它会处理报告应用程序的视图部分。清单 8 定义了新的报告 Groovlet：</P><A name=code8><B>清单 8. 重构后的数据库报告应用程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.sql.Sql
/**
 * forwards to passed in page
 */
def forward(page, req, res){
  dis = req.getRequestDispatcher(page);
  dis.forward(req, res);
}
sql = Sql.newInstance("jdbc:mysql://yourserver.anywhere/tiger", "scott",
        "tiger", "org.gjt.mm.mysql.Driver")
   
uptime = null
questions = null
insertnum = null
selectnum = null
updatenum = null
sql.eachRow("show status"){ status |
  if(status.variable_name == "Uptime"){
         uptime =  status[1]
	 request.setAttribute("uptime", uptime)
  }else if (status.variable_name == "Questions"){
         questions =  status[1]
	 request.setAttribute("questions", questions)
  }
}
request.setAttribute("qpm", Integer.valueOf(questions) / 
Integer.valueOf(uptime) )
sql.eachRow("show status like 'Com_%'"){ status |
    if(status.variable_name == "Com_insert"){
         insertnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_select"){
         selectnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_update"){
          updatenum =  Integer.valueOf(status[1])
    }
}
request.setAttribute("qinsert", 100 * (insertnum / Integer.valueOf(uptime)))
request.setAttribute("qselect", 100 * (selectnum / Integer.valueOf(uptime)))
request.setAttribute("qupdate", 100 * (updatenum / Integer.valueOf(uptime)))
forward("mysqlreport.gsp", request, response)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 8 中的代码应当是您相当熟悉的。我只是将以前应用程序中的所有 <CODE>println</CODE> 替换掉，并添加了 <CODE>forward</CODE> 函数来处理报告的视图部分。</P>
<P><A name=N10273><SPAN class=atitle3>添加视图部分</SPAN></A><BR>下一步是创建 GSP 来处理报告应用程序的视图。因为我是工程师而不是一个艺术家，所以我的视图是相当简单的 —— 一些 HTML 加上一个表，如清单 9 所示：</P><A name=code9><B>清单 9. 报告的视图部分</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;html&gt;&lt;head&gt;
&lt;title&gt;MySql Health Report&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table&gt;
&lt;tr&gt;
  &lt;td&gt;Database Uptime:&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("uptime")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Number of Queries:&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("questions")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Queries per Minute =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qpm")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Inserts =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qinsert")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Selects =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qselect")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;% Queries Updates =&lt;/td&gt;&lt;td&gt;&lt;% println
  "${request.getAttribute("qupdate")}" %&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>运行新的报告应当生成如图 3 所示的输出，数字会有变化。</P>
<P><A name=fig3><B>图 3. 重构后的报告应用程序的输出</B></A><BR><IMG height=347 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-pg03155/figure3.jpg" width=600 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N1029C><SPAN class=atitle2>结束语</SPAN></A><BR>如您所见，当所需要的功能相当简单并且需要尽快完成时，Groovlet 和 GSP 是进行服务器端开发的当然之选。这两个框架都特别灵活，并且其代码到视图的转化时间事实上是无可匹敌的。</P>
<P>不过，需要强调的是，Groovlet 不是 Strut 的替代品。GSP 框架不是<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">直接</I> 在速度上与其他产品竞争。GroovySql 不是 Hibernate 的替代品。而 Groovy 也不是 Java 语言的替代品。</P>
<P>无论如何，这些技术是补充，在大多数情况下，Groovy 是快速开发的更简单的一种选择。就像 GroovySql 是直接使用 JDBC 的替代方法一样，Groovlet 和 GSP 实际上是直接使用 Servlet API 的替代品。</P>
<P>下个月，我将探讨 GroovyMarkup 的奇妙世界。</P><img src ="http://www.blogjava.net/mstar/aggbug/6263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-17 10:57 <a href="http://www.blogjava.net/mstar/archive/2005/06/17/6263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 用 Groovy 更迅速地对 Java 代码进行单元测试</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6262.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6262.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6262.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE><ABSTRACT-EXTENDED>不久以前，developerWorks 的作者 Andrew Glover 撰写了一篇介绍 Groovy 的文章，该文章是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I> 系列的一部分，而 Groovy 是一个新提议的用于 Java 平台的标准语言。读者对这篇文章的反应非常热烈，所以我们决定开办这个专栏，提供使用这项热门新技术的实用指导。本文是第一期，将介绍使用 Groovy 和 JUnit 对 Java 代码进行单元测试的一个简单策略。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>开始之前，我首先要招认：我是一个单元测试狂。实际上，我总是无法编写足够的单元测试。如果我相当长一段时间都在进行开发，而 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">没有</I>编写相应的单元测试，我就会觉得紧张。单元测试给我信心，让我相信我的代码能够工作，而且我只要看一下，可以修改它，就不会害怕它会崩溃。 </P>
<P>而且，作为一个单元测试狂，我喜欢编写多余的测试用例。但是，我的兴奋不是来自 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编写</I>测试用例，而是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">看着</I>它们生效。所以，如果我能用更快的方式编写测试，我就能更迅速地看到它们的结果。这让我感觉更好。更快一些！ </P>
<P>后来，我找到了 Groovy，它满足了我的单元测试狂，而且至今为止，对我仍然有效。这种新语言给单元测试带来的灵活性，非常令人兴奋，值得认真研究。本文是介绍 Groovy 实践方面的新系列的第一部分，在文中，我将向您介绍使用 Groovy 进行单元测试的快乐。我从概述开始，概述 Groovy 对 Java 平台上的开发所做的独特贡献，然后转而讨论使用 Groovy 和 JUnit 进行单元测试的细节，其中重点放在 Groovy 对 JUnit 的 <CODE>TestCase</CODE> 类的扩展上。最后，我用一个实用的示例进行总结，用第一手材料向您展示如何把 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groovy</I> 的这些特性与 Eclipse 和 Maven 集成在一起。 </P>
<P><A name=N10061><SPAN class=atitle2>不要再坚持 Java 纯粹主义了！</SPAN></A><BR>在我开始介绍用 Groovy 进行单元测试的实际经验之前，我认为先谈谈一个更具一般性的问题 —— 它在您的开发工具箱中的位置，这非常重要。事实是，Groovy 不仅是运行在 Java 运行时环境（JRE）中的脚本语言，它还被提议作为用于 Java 平台的标准语言。正如您们之中的人已经从 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">alt.lang.jre</I> 系列（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）中学习到的，在为 Java 平台进行脚本编程的时候，有无数的选择，其中大多数是面向快速应用程序开发的高度灵活的环境。 </P>
<P>虽然有这么丰富的选择，但还是有许多开发人选择坚持自己喜欢的、最熟悉的范式：Java 语言。虽然大多数情况下，Java 编程都是很好的选择，但是它有一个非常重要的缺点，蒙住了只看见 Java 的好处的这些人的眼睛。正如一个智者曾经指出的： <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">如果您仅有的一个工具是一把锤子，那么您看每个问题时都会觉得它像是钉子</I>。我认为这句谚语道出了适用于软件开发的许多事实。 </P>
<P>虽然我希望用这个系列说服您 Java 不是也不应当是开发应用程序的惟一选择，但该脚本确实既有适用的地方也有不适用的地方。专家和新手的区别在于：知道什么时候 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">运用</I>该脚本，什么时候 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">避免</I>使用它。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10082><B>关于本系列</B></A><BR>把工具整合到开发实践中的关键是了解什么时候使用它，以及什么时候把它留在工具箱中。脚本语言能够成为工具包中极为强大的附件，但是只有正确地应用在适当的场合时才是这样。为了实现 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列文章这个目标，我专门研究了 Groovy 的一些实战，教给您什么时候怎样才能成功地应用它们。 </P></TD></TR></TBODY></TABLE></P>
<P>例如，对于高性能、事务密集型、企业级应用程序，Groovy 脚本通常不太适合；在这些情况下，您最好的选择 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可能</I>是普通的 J2EE 系统。但另一方面，一些脚本 —— 特别是用 Groovy 编写的脚本 —— 会非常有用，因为它能迅速地为小型的、非常特殊的、不是性能密集型的应用程序（例如配置系统/生成系统）快速制作原型。对于报表应用程序来说， Groovy 脚本也是近乎完美的选择，而最重要的是，对单元测试更是如此。 </P>
<P><A name=N10095><SPAN class=atitle2>为什么用 Groovy 进行单元测试？</SPAN></A><BR>是什么让 Groovy 比起其他脚本平台显得更具有吸引力呢？是它与Java 平台无缝的集成。还是因为它是基于 Java 的语言（不像其他语言，是对 JRE 的替代，因此可能基于旧版的处理器），对于 Java 开发人员来说，Groovy 意味着一条短得让人难以置信的学习曲线。而且一旦将这条学习曲线拉直，Groovy 就能提供一个无与伦比的快速开发平台。</P>
<P>从这个角度来说，Groovy 成功的秘密，在于它的语法 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">就是</I> Java 语法，但是规则更少。例如，Groovy 不要求使用分号，变量类型和访问修饰符也是可选的。而且，Groovy 利用了标准的 Java 库，这些都是您已经很熟悉的，包括 <CODE>Collections</CODE> 和 <CODE>File/IO</CODE>。而且，您还可以利用任何 Groovy 提供的 Java 库，包括 JUnit。 </P>
<P>事实上，令人放松的类 Java 语法、对标准 Java 库的重用以及快捷的生成-运行周期，这些都使 Groovy 成为快速开发单元测试的理想替代品。但是会说的不如会做的，还是让我们在代码中看看它的实际效果！</P>
<P><A name=N100AF><SPAN class=atitle2>JUnit 和 Groovy</SPAN></A><BR>用 Groovy 对 Java 代码进行单元测试简单得不能再简单了，有很多入门选择。最直接的选择是沿袭行业标准 —— JUnit。Unit 的简单性和其功能的强大都是无与伦比的，作为非常有帮助的 Java 开发工具，它的普遍性也是无与伦比的，而且没有什么能够阻挡 JUnit 和 Groovy 结合，所以为什么多此一举呢？实际上，只要您看过 JUnit 和 Groovy 在一起工作，我敢打赌您就永远再也不会回头！在这里，要记住的关键的事，您在 Java 语言中能用 JUnit 做到的事，在 Groovy 中用 JUnit 也全都能做到；但是需要的代码要少得多。</P>
<P><A name=N100B8><SPAN class=atitle3>入门</SPAN></A><BR>在您下载了 JUnit 和 Groovy（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）之后，您将有两个选择。第一个选择是编写普通的 JUnit 测试用例，就像以前一直做的那样，扩展 JUnit 令人称赞的 <CODE>TestCase</CODE>。第二个选择是应用 Groovy 简洁的 <CODE>GroovyTestCase</CODE> 扩展，它会扩展 JUnit 的 <CODE>TestCase</CODE>。第一个选项是您最快的成功途径，它拥有最多 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">与 Java 类似的</I>相似性。而另一方面，第二个选择则把您推进了 Groovey 的世界，这个选择有最大的敏捷性。 </P>
<P>开始的时候，我们来想像一个 Java 对象，该对象对指定的 <CODE>string</CODE> 应用了一个过滤器，并根据匹配结果返回 <CODE>boolean</CODE> 值。该过滤器可以是简单的 <CODE>string</CODE> 操作，例如 <CODE>indexOf()</CODE>，也可以更强大一些，是正则表达式。可能要通过 <CODE>setFilter()</CODE> 方法在运行时设置将使用的过滤器， <CODE>apply()</CODE> 方法接受要过滤的 <CODE>string</CODE>。清单 1 用普通的 Java 代码显示了这个示例的 <CODE>Filter</CODE> 接口： </P><A name=code16><B>清单 1. 一个简单的 Java Filter 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public interface Filter {
  void setFilter(String fltr);  
  boolean applyFilter(String value);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们的想法是用这个特性从大的列表中过滤掉想要的或者不想要的包名。所以，我建立了两个实现： <CODE>RegexPackageFilter</CODE> 和 <CODE>SimplePackageFilter</CODE>。 </P>
<P>把 Groovy 和 JUnit 的强大功能与简单性结合在一起，就形成了如清单 2 所示的简洁的测试套件：</P><A name=code16><B>清单 2. 用 JUunit 制作的 Groovy RegexFilterTest </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import junit.framework.TestCase
import com.vanward.sedona.frmwrk.filter.impl.RegexPackageFilter

class RegexFilterTest extends TestCase {  

  void testSimpleRegex() {
    fltr = new RegexPackageFilter()
    fltr.setFilter("java.*")
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)		
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>不管您是否熟悉 Groovy，清单 2 中的代码对您来说应当很面熟，因为它只不过是没有分号、访问修饰符或变量类型的 Java 代码而已！上面的 JUnit 测试中有一个测试用例 <CODE>testSimpleRegex()</CODE>，它试图断言 <CODE>RegexPackageFilter</CODE> 用正则表达式 <CODE>"java.*"</CODE> 正确地找到了与 “ <CODE>java.lang.String</CODE>”匹配的对象。 </P>
<P><A name=N10130><SPAN class=atitle2>Groovy 扩展了 JUnit</SPAN></A><BR>扩展 JUnit 的 <CODE>TestCase</CODE> 类，加入附加特性，实际上是每个 JUnit 扩展通常采用的技术。例如，DbUnit 框架（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）提供了一个方便的 <CODE>DatabaseTestCase</CODE> 类，能够比以往任何时候都更容易地管理数据库的状态，还有重要的 <CODE>MockStrutsTestCase</CODE>（来自 StrutsTestCase 框架；请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），它生成虚拟的 <CODE>servlet</CODE> 容器，用来执行 struts 代码。这两个强大的框架都极好地扩展了 JUnit，提供了 JUnit 核心代码中所没有的其他特性；而现在 Groovy 来了，它也是这么做的！ </P>
<P>与 StrutsTestCase 和 DbUnit 一样，Groovy 对 JUnit 的 <CODE>TestCase</CODE> 的扩展给您的工具箱带来了一些重要的新特性。这个特殊的扩展允许您通过 <CODE>groovy</CODE> 命令运行测试套件，而且提供了一套新的 <CODE>assert</CODE> 方法。可以用这些方法很方便地断言脚本的运行是否正确，以及断言各种数组类型的长度和内容等。 </P>
<P><A name=N10160><SPAN class=atitle2>享受 GroovyTestCase 的快乐</SPAN></A><BR>了解 <CODE>GroovyTestCase</CODE> 的能力最好的办法，莫过于实际看到它的效果。在清单 3 中，我已经编写了一个新的 <CODE>SimpleFilterTest</CODE>，但是这次我要扩展 <CODE>GroovyTestCase</CODE> 来实现它： </P><A name=code16><B>清单 3. 一个真正的 GroovyTestCase</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.util.GroovyTestCase
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter

class SimpleFilterTest extends GroovyTestCase {
	
  void testSimpleJavaPackage() {
    fltr = new SimplePackageFilter()
    fltr.setFilter("java.")		
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)
  }	
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>请注意，可以通过命令行来运行该测试套件，没有运行基于 Java 的 JUnit 测试套件所需要的 <CODE>main()</CODE> 方法。实际上，如果我用 Java 代码编写上面的 <CODE>SimpleFilterTest</CODE>，那么代码看起来会像清单 4 所示的那样： </P><A name=code16><B>清单 4. 用 Java 代码编写的同样的测试用例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import junit.framework.TestCase;
import com.vanward.sedona.frmwrk.filter.Filter;
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter;

public class SimplePackageFilterTest extends TestCase {       

   public void testSimpleRegex() {
	Filter fltr = new SimplePackageFilter();
	fltr.setFilter("java.");
	boolean val = fltr.applyFilter("java.lang.String");
	assertEquals("value should be true", true, val);
   }
	
   public static void main(String[] args) {
 	junit.textui.TestRunner.run(SimplePackageFilterTest.class);
   }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10196><SPAN class=atitle3>用断言进行测试</SPAN></A><BR>除了可以让您通过命令行运行测试之外， <CODE>GroovyTestCase</CODE> 还向您提供了一些特别方便的 <CODE>assert</CODE> 方法。例如， <CODE>assertArrayEquals</CODE>，它可以检查两个数据中对应的每一个值和各自的长度，从而断言这两个数据是否相等。从清单 5 的示例开始，就可以看到 Groovy 断言的实际效果，清单 5 是一个简洁的基于 Java 的方法，它把 <CODE>string</CODE> 分解成数组。（请注意，我可能使用了 Java 1.4 中新添加的 <CODE>string</CODE> 特性编写以下的示例类。我采用 Jakarta Commons <CODE>StringUtils</CODE> 类来确保与 Java 1.3 的后向兼容性。） </P><A name=code16><B>清单 5. 定义一个 Java StringSplitter 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.apache.commons.lang.StringUtils;

public class StringSplitter {
  public static String[] split(final String input, final String separator){
   return StringUtils.split(input, separator);
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 6 展示了用 Groovy 测试套件及其对应的 <CODE>assertArrayEquals</CODE> 方法对这个类进行测试是多么简单： </P><A name=code16><B>清单 6. 使用 GroovyTestCase 的 assertArrayEquals 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import groovy.util.GroovyTestCase
import com.vanward.resource.string.StringSplitter

class StringSplitTest extends GroovyTestCase {
	
  void testFullSplit() {
    splitAr = StringSplitter.split("groovy.util.GroovyTestCase", ".")		
    expect = ["groovy", "util", "GroovyTestCase"].toArray()
    assertArrayEquals(expect, splitAr)		
  }	
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101D4><SPAN class=atitle2>其他方法</SPAN></A><BR>Groovy 可以让您单独或成批运行测试。使用 <CODE>GroovyTestCase</CODE> 扩展，运行单个测试毫不费力。只要运行 <CODE>groovy</CODE> 命令，后面跟着要运行的测试套件即可，如清单 7 所示： </P><A name=code16><B>清单 7. 通过 groovy 命令运行 GroovyTestCase 测试用例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
$./groovy test/com/vanward/sedona/frmwrk/filter/impl/SimpleFilterTest.groovy
.
Time: 0.047

OK (1 test)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>Groovy 还提供了一个标准的 JUnit 测试套件，叫作 <CODE>GroovyTestSuite</CODE>。只要运行该测试套件，把脚本的路径传给它，它就会运行脚本，就像 <CODE>groovy</CODE> 命令一样。这项技术的好处是，它可以让您在 IDE 中运行脚本。例如，在 Eclipse 中，我只是为示例项目建立了一个新的运行配置（一定要选中 “Include external jars when searching for a main class”），然后找到主类 <CODE>groovy.util.GroovyTestSuite</CODE>，如图 1 所示： </P>
<P><A name=N10201><B>图 1. 用 Eclipse 运行 GroovyTestSuite</B></A><BR><IMG height=387 alt="图 1. 用 Eclipse 运行 GroovyTestSuite" src="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/pic1.jpg" width=600 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>在图 2 中，您可以看到当点击 Arguments 标签，写入脚本的路径时，会发生了什么：</P>
<P><A name=N10215><B>图 2. 在 Eclipse 中指定脚本的路径</B></A><BR><IMG height=389 alt="图 2. 在 Eclipse 中指定脚本的路径" src="http://www-128.ibm.com/developerworks/cn/java/j-pg11094/pic2.jpg" width=600 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>运行一个自己喜欢的 JUnit Groovy 脚本，实在是很简单，只要在 Eclipse 中找到对应的运行配置就可以了。</P>
<P><A name=N10227><SPAN class=atitle2>用 Ant 和 Maven 进行测试</SPAN></A><BR>这个像 JUnit 一样的框架的美妙之处还在于，它可以把整套测试作为 build 的一部分运行，不需要人工进行干预。随着越来越多的人把测试用例加入代码基（code base），整体的测试套件日益增长，形成一个极好的回归平台（regression platform）。更妙的是，Ant 和 Maven 这样的 build 框架已经加入了报告特性，可以归纳 Junit 批处理任务运行的结果。</P>
<P>把一组 Groovy 测试用例整合到某一个构建中的最简单的方法是把它们编译成普通的 Java 字节码，然后把它们包含在 Ant 和 Maven 提供的标准的 Junit 批命令中。幸运的是，Groovy 提供了一个 Ant 标签，能够把未编译的 Groovy 脚本集成到字节码中，这样，把脚本转换成有用的字节码的处理工作就变得再简单不过。例如，如果正在使用 Maven 进行构建工作，那么只需在maven.xml 文件中添加两个新的目标、在 project.xml 中添加两个新的相关性、在 build.properties 文件中添加一个简单的标志就可以了。</P>
<P>我要从更新 maven.xml 文件开始，用新的目标来编译示例脚本，如清单 8 所示：</P><A name=code16><B>清单 8. 定义 Groovyc 目标的新 maven.xml 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
 &lt;goal name="run-groovyc" prereqs="java:compile,test:compile"&gt;
   
   &lt;path id="groovy.classpath"&gt;
     &lt;pathelement path="${maven.build.dest}"/&gt;
     &lt;pathelement path="target/classes"/&gt;
     &lt;pathelement path="target/test-classes"/&gt;
     &lt;path refid="maven.dependency.classpath"/&gt;
   &lt;/path&gt;

 &lt;taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc"&gt;
    &lt;classpath refid="groovy.classpath"/&gt;
 &lt;/taskdef&gt;

 &lt;groovyc destdir="${basedir}/target/test-classes" srcdir="${basedir}/test/groovy" 
          listfiles="true"&gt;
	&lt;classpath refid="groovy.classpath"/&gt;
 &lt;/groovyc&gt;

 &lt;/goal&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上面代码中发生了以下几件事。第一，我定义一个名为 <CODE>run-groovyc</CODE> 的新目标。该目标有两个前提条件， <CODE>java:compile</CODE> 编译示例源代码， <CODE>test:compile</CODE> 编译普通的 Java-JUnit 类。我还用 <CODE>&lt;path&gt;</CODE> 标签创建了一个 classpath。在该例中，classpath 把 build 目录（保存编译后的源文件）和与它相关的所有依存关系（即 JAR 文件）整合在一起。接着，我还用 <CODE>&lt;taskdef&gt;</CODE> Ant 标签定义了 <CODE>groovyc</CODE> 任务。 </P>
<P>而且，请您注意我在 classpath 中是如何告诉 Maven 到哪里去找 <CODE>org.codehaus.groovy.ant.Groovyc</CODE> 这个类。在示例的最后一行，我定义了 <CODE>&lt;groovyc&gt;</CODE> 标签，它会编译在 <CODE>test/groovy</CODE> 目录中发现的全部 Groovy 脚本，并把生成的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">.class</I> 文件放在 <CODE>target/test-classes</CODE> 目录中。 </P>
<P><A name=N10272><SPAN class=atitle3>一些重要细节</SPAN></A><BR>为了编译 Groovy 脚本，并运行生成的字节码，我必须要通过 project.xml 文件定义两个新的依存关系（ <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">groovy</I> 和 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">asm</I>），如清单 9 所示： </P><A name=code16><B>清单 9. project.xml 文件中的新的依存关系</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;dependency&gt;
    &lt;groupId&gt;groovy&lt;/groupId&gt;
    &lt;id&gt;groovy&lt;/id&gt;
    &lt;version&gt;1.0-beta-6&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;dependency&gt;
    &lt;groupId&gt;asm&lt;/groupId&gt;
    &lt;id&gt;asm&lt;/id&gt;
    &lt;version&gt;1.4.1&lt;/version&gt;
  &lt;/dependency&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>一旦将脚本编译成普遍的 Java 字节码，那么任何标准的 JUnit 运行器就都能运行它们。因为 Ant 和 Maven 都拥有 JUnit 运行器标签，所以下一步就是让 JUnit 挑选新编译的 Groovy 脚本。而且，因为 Maven 的 JUnit 运行器使用模式匹配来查找要运行的测试套件，所以需要在 build.properties 文件中添加一个特殊标记，如清单 10 所示，该标记告诉 Maven 去搜索类而不是搜索 .java 文件。</P><A name=code16><B>清单 10. Maven 项目的 build.properties 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
 maven.test.search.classdir = true
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>最后，我在 maven.xml 文件中定义了一个测试目标（ <CODE>goal</CODE>），如清单 11 所示。这样做可以确保 在单元测试运行之前，使用新的 <CODE>run-groovyc</CODE> 目标编译 Groovy 脚本。 </P><A name=code16><B>清单 11. maven.xml 的新目标</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;goal name="test"&gt;
    &lt;attainGoal name="run-groovyc"/&gt;
    &lt;attainGoal name="test:test"/&gt;    	
  &lt;/goal&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102B0><SPAN class=atitle3>最后一个，但并非最不重要</SPAN></A><BR>有了新定义的两个目标（一个用来编译脚本，另外一个用来运行 Java 和 Groovy 组合而成的单元测试），剩下的事就只有运行它们，检查是不是每件事都顺利运行！</P>
<P>在清单 12 中，您可以看到，当我运行 Maven，给 <CODE>test</CODE> 传递目标之后，会发生了什么，它首先包含 <CODE>run-groovyc</CODE> 目标（而该目标恰好还包含 <CODE>java:compile</CODE> 和 <CODE>test:compile</CODE> 这两个目标），然后包含 Maven 中自带的标准的 <CODE>test:test</CODE> 目标。请注意观察目标 <CODE>test:test</CODE> 是如何处理新生成的 Groovy 脚本（在该例中，是新 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编译的</I> Groovy 脚本） <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">以及</I>普通的 Java JUnit 测试。 </P><A name=code16><B>清单 12. 运行新的测试目标</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
$ ./maven test

test:
java:compile:
    [echo] Compiling to /home/aglover/dev/target/classes
    [javac] Compiling 15 source files to /home/aglover/dev/target/classes

test:compile:
    [javac] Compiling 4 source files to /home/aglover/dev/target/test-classes

run-groovyc:
    [groovyc] Compiling 2 source files to /home/aglover/dev/target/test-classes
    [groovyc] /home/aglover/dev/test/groovy/test/RegexFilterTest.groovy
    [groovyc] /home/aglover/dev/test/groovy/test/SimpleFilterTest.groovy

test:test:    
    [junit] Running test.RegexFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.656 sec    
    [junit] Running test.SimpleFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.609 sec
    [junit] Running test.SimplePackageFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.578 sec    
BUILD SUCCESSFUL
Total time: 42 seconds
Finished at: Tue Sep 21 17:37:08 EDT 2004
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N102E5><SPAN class=atitle2>结束语</SPAN></A><BR>在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列的第一期中，您学习了 Groovy 这个令人兴奋的脚本语言最实用的应用当中的一个。对于越来越多开发人员而言，单元测试是开发过程的重要组成部分；而使用 Groovy 和 JUnit 对 Java 代码进行测试就变成了轻而易举的事情。 </P>
<P>Groovy 简单的语法、内部的灵活性，使其成为迅速编写有效的 JUnit 测试、将测试整合到自动编译中的一个优秀平台。对于像我一样为代码质量发狂的人来说，这种组合极大地减少了我的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">神经紧张</I>，还让我可以得到我想做得最好的东西：编写“防弹”软件。快点行动吧。 </P>
<P>因为这是一个新的系列，所以我非常希望您能一起来推动它前进。如果您对 Groovy 有什么想了解的事情，请 <A href="mailto:aglover@vanwardtechnologies.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">发邮件给我</A>，让我知道您的要求！我希望您会继续支持本系列的下一期，在下期中，我将介绍用 Groovy 进行 Ant 脚本编程。 </P><img src ="http://www.blogjava.net/mstar/aggbug/6262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-17 10:53 <a href="http://www.blogjava.net/mstar/archive/2005/06/17/6262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 Groovy: 在 Java 应用程序中加一些 Groovy 进来</title><link>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 17 Jun 2005 02:51:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6261.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/17/6261.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6261.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6261.html</trackback:ping><description><![CDATA[<P align=left><!--StartFragment --> <SPAN class=atitle2>嵌入简单的、易于编写的脚本，从而利用 Groovy 的简单性</SPAN><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 中级</P></TD></TR></TBODY></TABLE></P>
<P align=left><A href="http://www-128.ibm.com/developerworks/cn/java/j-pg05245/index.html?ca=dwcn-newsletter-java#author1"><NAME>Andrew Glover</NAME></A><BR>CTO, Vanward Technologies<BR>2005 年 6 月 13 日</P>
<BLOCKQUOTE>
<P align=left><ABSTRACT-EXTENDED>您有没有想过在自己相对复杂的 Java 程序中嵌入 Groovy 简单的、易于编写的脚本呢？在这一期 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实战 Groovy</I> 系列文章中，Andrew Glover 将介绍把 Groovy 集成到 Java 代码中的多种方法，并解释在什么地方、什么时候适合这么做。</ABSTRACT-EXTENDED></P></BLOCKQUOTE>
<P align=left>如果您一直在阅读这个系列，那么您应该已经看到有各种各样使用 Groovy 的有趣方式，Groovy 的主要优势之一就是它的生产力。Groovy 代码通常要比 Java 代码更容易编写，而且编写起来也更快，这使得它有足够的资格成为开发工作包中的一个附件。在另一方面，正如我在这个系列中反复强调的那样，Groovy 并不是 —— 而且也不打算成为 —— Java 语言的替代。所以，这里存在的问题是，能否把 Groovy 集成到 Java 的编程实践中？或者说这样做有什么用？<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">什么时候</I> 这样做有用？</P>
<P align=left>这个月，我将尝试回答这个问题。我将从一些熟悉的事物开始，即从如何将 Groovy 脚本编译成与 Java 兼容的类文件开始，然后进一步仔细研究 Groovy 的编译工具（<CODE>groovyc</CODE>）是如何让这个奇迹实现的。了解 Groovy 在幕后做了什么是在 Java 代码中使用 Groovy 的第一步。</P>
<P align=left>注意，本月示例中演示的一些编程技术是 <CODE>Groovlets</CODE> 框架和 Groovy 的 <CODE>GroovyTestCase</CODE> 的核心，这些技术我在前面的文章中已经讨论过。 </P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDAATGX><B>关于本系列</B></A><BR>把任何工具集成到自己的开发实践的关键就是知道什么时候使用它，而什么时候应当把它留在箱子里。脚本语言能够成为工具箱中极为强大的附件，但只在将它恰当应用到合适场景时才这样。为此，<A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E5%AE%9E%E6%88%98+Groovy%3A" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>实战 Groovy</I></A> 的一系列文章专门探索了 Groovy 的实际应用，并告诉您什么时候应用它们，以及如何成功地应用它们。</P></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDALTGX><SPAN class=atitle2>天作之合？</SPAN></A><BR>在本系列中以前的文章中，当我介绍如何 <A href="http://www.ibm.com/developerworks/java/library/j-pg11094/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">用 Groovy 测试普通 Java 程序</A> 的时候，您可能已经注意到一些奇怪的事：我 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">编译了</I> 那些 Groovy 脚本。实际上，我将 groovy 单元测试编译成普通的 Java .class 文件，然后把它们作为 Maven 构建的一部分来运行。</P>
<P align=left>这种编译是通过调用 <CODE>groovyc</CODE> 命令进行的，该命令将 Groovy 脚本编译成普通的 Java 兼容的 .class 文件。例如，如果脚本声明了一个类，那么调用 <CODE>groovyc</CODE> 会生成至少三个 .class 。文件本身会遵守标准的 Java 规则：.class 文件名称要和声明的类名匹配。 </P>
<P align=left>作为示例，请参见清单 1，它创建了一个简单的脚本，脚本声明了几个类。然后，您自己就可以看出 <CODE>groovyc</CODE> 命令生成的结果：</P>
<P align=left><A name=code1><B>清单 1. Groovy 中的类声明和编译</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

class Person {
  fname
  lname
  age
  address
  contactNumbers
  String toString(){
   
    numstr = new StringBuffer()
    if (contactNumbers != null){
     contactNumbers.each{
       numstr.append(it)
       numstr.append(" ")
     }
    }
    "first name: " + fname + " last name: " + lname + 
    " age: " + age + " address: " + address + 
    " contact numbers: " + numstr.toString()
  }
}
class Address {
  street1
  street2
  city
  state
  zip
  String toString(){
   "street1: " + street1 + " street2: " + street2 +
     " city: " + city + " state: " + state + " zip: " + zip
  }
}
class ContactNumber {
  type
  number
  String toString(){
   "Type: " + type + " number: " + number 
  }
}
nums = [new ContactNumber(type:"cell", number:"555.555.9999"), 
	new ContactNumber(type:"office", number:"555.555.5598")]
addr = new Address(street1:"89 Main St.", street2:"Apt #2", 
	city:"Utopia", state:"VA", zip:"34254")
pers = new Person(fname:"Mollie", lname:"Smith", age:34, 
	address:addr, contactNumbers:nums)
println pers.toString()
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>在清单 1 中，我声明了三个类 —— <CODE>Person</CODE>、<CODE>Address</CODE> 和 <CODE>ContactNumber</CODE>。之后的代码根据这些新定义的类型创建对象，然后调用 <CODE>toString()</CODE> 方法。迄今为止，Groovy 中的代码还非常简单，但现在来看一下清单 2 中 <CODE>groovyc</CODE> 产生什么样的结果：</P>
<P align=left><A name=code2><B>清单 2. groovyc 命令生成的类</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy
$ ls -ls  
total 15
 4 -rwxrwxrwx+ 1 aglover  user   3317 May  3 21:12 Address.class
 3 -rwxrwxrwx+ 1 aglover  user   3061 May  3 21:12 BusinessObjects.class
 3 -rwxrwxrwx+ 1 aglover  user   2815 May  3 21:12 ContactNumber.class
 1 -rwxrwxrwx+ 1 aglover  user   1003 May  3 21:12 
   Person$_toString_closure1.class
 4 -rwxrwxrwx+ 1 aglover  user   4055 May  3 21:12 Person.class
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>哇！<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">五个</I> .class 文件！我们了解 <CODE>Person</CODE>、<CODE>Address</CODE> 和 <CODE>ContactNumber</CODE> 文件的意义，但是其他两个文件有什么作用呢？</P>
<P align=left>研究发现，<CODE>Person$_toString_closure1.class</CODE> 是 <CODE>Person</CODE> 类的 <CODE>toString()</CODE> 方法中发现的闭包的结果。它是 <CODE>Person</CODE> 的一个内部类，但是 <CODE>BusinessObjects.class</CODE> 文件是怎么回事 —— 它可能是什么呢？</P>
<P align=left>对 <A href="http://www-128.ibm.com/developerworks/cn/java/j-pg05245/index.html?ca=dwcn-newsletter-java#code1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 1</A> 的深入观察指出：我在脚本主体中编写的代码（声明完三个类之后的代码）变成一个 .class 文件，它的名称采用的是脚本名称。在这个例子中，脚本被命名为 <CODE>BusinessObjects.groovy</CODE>，所以，类定义中没有包含的代码被编译到一个名为 <CODE>BusinessObjects</CODE> 的 .class 文件。</P>
<P align=left><A name=IDAEXGX><SPAN class=atitle3>反编译</SPAN></A><BR>反编译这些类可能会非常有趣。由于 Groovy 处于代码顶层，所以生成的 .java 文件可能相当巨大；不过，您应当注意的是 Groovy 脚本中声明的类（如 <CODE>Person</CODE>） 与类之外的代码（比如 <CODE>BusinessObjects.class</CODE> 中找到的代码）之间的区别。在 Groovy 文件中定义的类完成了 <CODE>GroovyObject</CODE> 的实现，而在类之外定义的代码则被绑定到一个扩展自 <CODE>Script</CODE> 的类。 </P>
<P align=left>例如，如果研究由 BusinessObjects.class 生成的 .java 文件，可以发现：它定义了一个 <CODE>main()</CODE> 方法和一个 <CODE>run()</CODE> 方法。不用惊讶， <CODE>run()</CODE> 方法包含我编写的、用来创建这些对象的新实例的代码，而 <CODE>main()</CODE> 方法则调用 <CODE>run()</CODE> 方法。</P>
<P align=left>这个细节的全部要点再一次回到了：对 Groovy 的理解越好，就越容易把它集成到 Java 程序中。有人也许会问：“为什么我要这么做呢？”好了，我们想说您用 Groovy 开发了一些很酷的东西；那么如果能把这些东西集成到 Java 程序中，那不是很好吗？ </P>
<P align=left>只是为了讨论的原因，我首先<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">试图</I> 用 Groovy 创建一些有用的东西，然后我再介绍把它嵌入到普通 Java 程序中的各种方法。</P>
<P align=left><A name=IDAZYLX><SPAN class=atitle2>再制作一个音乐 Groovy</SPAN></A><BR>我热爱音乐。实际上，我的 CD 收藏超过了我计算机图书的收藏。多年以来，我把我的音乐截取到不同的计算机上，在这个过程中，我的 MP3 收藏乱到了这样一种层度：只是表示品种丰富的音乐目录就有一大堆。</P>
<P align=left>最近，为了让我的音乐收藏回归有序，我采取了第一步行动。我编写了一个快速的 Groovy 脚本，在某个目录的 MP3 收藏上进行迭代，然后把每个文件的详细信息（例如艺术家、专辑名称等）提供给我。脚本如清单 3 所示：</P>
<P align=left><A name=code3><B>清单 3. 一个非常有用的 Groovy 脚本</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.vanward.groovy

import org.farng.mp3.MP3File
import groovy.util.AntBuilder

class Song {
  
  mp3file
  Song(String mp3name){   
    mp3file = new MP3File(mp3name)   
  }
  getTitle(){
    mp3file.getID3v1Tag().getTitle()
  }
  getAlbum(){
    mp3file.getID3v1Tag().getAlbum()
  }
  getArtist(){
    mp3file.getID3v1Tag().getArtist()
  }
  String toString(){
    "Artist: " + getArtist() + " Album: " + 
      getAlbum() + " Song: " + getTitle()
  }
  static getSongsForDirectory(sdir){
    println "sdir is: " + sdir
    ant = new AntBuilder()
    scanner = ant.fileScanner {
       fileset(dir:sdir) {
         include(name:"**/*.mp3")
       }
    }
    songs = []
    for(f in scanner){   
      songs &lt;&lt; new Song(f.getAbsolutePath())   
    }
    return songs
  }
}
songs = Song.getSongsForDirectory(args[0])
songs.each{
 println it
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left>正如您所看到的，脚本非常简单，对于像我这样的人来说特别有用。而我要做的全部工作只是把某个具体的目录名传递给它，然后我就会得到该目录中每个 MP3 文件的相关信息（艺术家名称、歌曲名称和专辑） 。</P>
<P align=left>现在让我们来看看，如果要把这个干净的脚本集成到一个能够通过数据库组织音乐甚至播放 MP3 的普通 Java 程序中，我需要做些什么。</P>
<P align=left><A name=IDALZLX><SPAN class=atitle2>Class 文件是类文件</SPAN></A><BR>正如前面讨论过的，我的第一个选项可能只是用 <CODE>groovyc</CODE> 编译脚本。在这个例子中，我期望 <CODE>groovyc</CODE> 创建 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">至少</I> 两个 .class 文件 —— 一个用于 <CODE>Song</CODE> 类，另一个用于 <CODE>Song</CODE> 声明之后的脚本代码。</P>
<P align=left>实际上，<CODE>groovyc</CODE> 可能创建 5 个 .class 文件。这是与 <CODE>Songs.groovy</CODE> 包含三个闭包有关，两个闭包在 <CODE>getSongsForDirectory()</CODE> 方法中，另一个在脚本体中，我在脚本体中对 <CODE>Song</CODE> 的集合进行迭代，并调用 <CODE>println</CODE>。</P>
<P align=left>因为 .class 文件中有三个实际上是 Song.class 和 Songs.class 的内部类，所以我只需要把注意力放在两个 .class 文件上。Song.class 直接映射到 Groovy 脚本中的 <CODE>Song</CODE> 声明，并实现了 <CODE>GroovyObject</CODE>，而 Songs.class 则代表我在定义 <CODE>Song</CODE> 之后编写的代码，所以也扩展了 <CODE>Script</CODE>。</P>
<P align=left>此时此刻，关于如何把新编译的 Groovy 代码集成到 Java 代码，我有两个选择：可以通过 Songs.class 文件中的 <CODE>main()</CODE> 方法运行代码 （因为它扩展了 <CODE>Script</CODE>），或者可以将 Song.class 包含到类路径中，就像在 Java 代码中使用其他对象一样使用它。 </P>
<P align=left><A name=IDAW1LX><SPAN class=atitle2>变得更容易些</SPAN></A><BR>通过 <CODE>java</CODE> 命令调用 Songs.class 文件非常简单，只要您记得把 Groovy 相关的依赖关系和 Groovy 脚本需要的依赖关系包含进来就可以。把 Groovy 需要的类全都包含进来的最简单方法就是把包含全部内容的 Groovy 可嵌入 jar 文件添加到类路径中。在我的例子中，这个文件是 groovy-all-1.0-beta-10.jar。要运行 Songs.class，需要记得包含将要用到的 MP3 库（jid3lib-0.5.jar&gt;），而且因为我使用 <CODE>AntBuilder</CODE>，所以我还需要在类路径中包含 <CODE>Ant</CODE>。清单 4 把这些放在了一起：</P>
<P align=left><A name=code4><B>清单 4. 通过 Java 命令行调用 Groovy</B></A><BR></P>
<P align=left>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
c:\dev\projects&gt;java -cp  ./target/classes/;c:/dev/tools/groovy/
  groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar;
  C:/dev/projects-2.0/jid3lib-0.5.jar  
  com.vanward.groovy.Songs c:\dev09\music\mp3s
Artist: U2 Album: Zooropa Song: Babyface
Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind
Artist: James Taylor Album: Greatest Hits Song: Fire and Rain
Artist: U2 Album: Zooropa Song: Lemon
Artist: James Taylor Album: Greatest Hits Song: Country Road
Artist: James Taylor Album: Greatest Hits Song: Don't Let Me 
  Be Lonely Tonight
Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others
Artist: Paul Simon Album: Graceland Song: Under African Skies
Artist: Paul Simon Album: Graceland Song: Homeless
Artist: U2 Album: Zooropa Song: Dirty Day
Artist: Paul Simon Album: Graceland Song: That Was Your Mother
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P align=left><A name=IDAS2LX><SPAN class=atitle2>把 Groovy 嵌入 Java 代码</SPAN></A><BR>虽然命令行的解决方案简单有趣，但它并不是所有问题的最终解决方案。如果对更高层次的完善感兴趣，那么可能将 MP3 歌曲工具直接导入 Java 程序。在这个例子中，我想