﻿<?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-鹰翔宇空-文章分类-opensource</title><link>http://www.blogjava.net/TrampEagle/category/9793.html</link><description>学习和生活
</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 08:52:34 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 08:52:34 GMT</pubDate><ttl>60</ttl><item><title>Velocity简介</title><link>http://www.blogjava.net/TrampEagle/articles/49340.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 31 May 2006 14:03:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/49340.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/49340.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/49340.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/49340.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/49340.html</trackback:ping><description><![CDATA[Velocity简介<br />原文引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=593&amp;page=1">http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=593&amp;page=1</a><br /><br />Velocity是一个开放源吗的模版引擎，由apache.org小组负责开发，现在最新的版本是Velocity1.3.1<br /><a href="http://&lt;a%20target=_blank%20href=http://jakarta.apache.org/velocity/index.html&gt;http://jakarta.apache.org/velocity/index.html&lt;/a&gt;" target="_blank"><font color="#000000">http://jakarta.apache.org/velocity/index.html </font></a>可以了解Velocity的最新信息。<br />    Velocity允许我们在模版中设定变量，然后在运行时，动态的将数据插入到模版中，替换这些变量。<br />    例如：<br /><br /><div class="codeStyle"><ol><li></li><li>    &lt;<font color="#ff0000">HTML</font>&gt; <br /></li><li>        &lt;BODY&gt; <br /></li><li>            HELLO $CUSTOMERNAME <br /></li><li>        &lt;/BODY&gt; <br /></li><li>    &lt;/<font color="#ff0000">HTML</font>&gt; </li></ol></div><br />    我们可以在运行时得到客户的名字，然后把它插入到这个模版中替换变量$CUSTOMERNAME，整个替换过程是由Velocity进行控制的，而且java的调用代码也非常简单<br />    如我们可以在java代码中这样调用<br /><br /><div class="codeStyle"><ol><li></li><li>    <i><font color="#339900">//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目录</font></i><br /></li><li>    Velocity.init(<font color="#ff33ff">"D:\\Template\\resource\\jt.properties"</font>); <br /></li><li>    <i><font color="#339900">//模版文件名，模版文件所在的路径在上一条语句中已经设置了</font></i><br /></li><li>    Template template =Velocity.getTemplate(<font color="#ff33ff">"hello.vm"</font>, <font color="#ff33ff">"gb2312"</font>);  <br /></li><li>    <i><font color="#339900">//实例化一个Context</font></i><br /></li><li>    VelocityContext context = <b><font color="#0000ff">new</font></b> VelocityContext();     <br /></li><li>    <i><font color="#339900">//把模版变量的值设置到context中</font></i><br /></li><li>    context.put(<font color="#ff33ff">"CUSTOMERNAME"</font>, <font color="#ff33ff">"My First Template Engine ---- Velocity."</font>);         <br /></li><li>    <i><font color="#339900">//开始模版的替换</font></i><br /></li><li>    template.merge(context, writer); <br /></li><li></li><li>    <i><font color="#339900">//写到文件中</font></i><br /></li><li>    <font color="#ff0000">PrintWriter</font> filewriter = <b><font color="#0000ff">new</font></b><font color="#ff0000">PrintWriter</font>(<b><font color="#0000ff">new</font></b><font color="#ff0000">FileOutputStream</font>(outpath),<b><font color="#0000ff">true</font></b>); <br /></li><li>    filewriter.println(writer.toString()); <br /></li><li>    filewriter.close(); </li></ol></div><br />    这就是整个java的代码，非常的简单。如果我们有多个模版变量，我们仅需要把这些模版变量的值设置到context中。<br />    <br />    下面我们简单的分析一下，Velocity引擎读取模板文件时，它直接输出文件中所有的文本，但以$字符开头的除外，$符号标识着一个模版变量位置，context.put("CUSTOMERNAME", "My First Template Engine ---- Velocity.");当Velocity模板引擎解析并输出模板的结果时，模板中所有出现$CUSTOMERNAME的地方都将插入客户的名字，即被加入到VelocityContext的对象的toString()方法返回值将替代Velocity变量（模板中以$开头的变量）。<br />    模板引擎中最强大、使用最频繁的功能之一是它通过<b>内建的映像</b>（Reflection）引擎查找对象信息的能力。这个映像引擎允许用一种方便的Java“.”类似的操作符，提取任意加入到VelocityContext的对象的任何公用方法的值，或对象的任意数据成员。映像引擎还带来了另外一个改进：快速引用JavaBean的属性。使用JavaBean属性的时候，我们可以忽略get方法和括号。请看下面这个模板的例子。<br /><br /><div class="codeStyle"><ol><li></li><li>    &lt;<font color="#ff0000">HTML</font>&gt; <br /></li><li>        &lt;BODY&gt; <br /></li><li>            <font color="#ff0000">Name</font>:         $Customer.<font color="#ff0000">Name</font>() <br /></li><li>            Address:    $Customer.Address() <br /></li><li>            Age:        $Customer.Age() <br /></li><li>        &lt;/BODY&gt; <br /></li><li>    &lt;/<font color="#ff0000">HTML</font>&gt; </li></ol></div><br />    java的代码：    <br /><br /><div class="codeStyle"><ol><li></li><li>    <i><font color="#339900">//设置客户信息</font></i><br /></li><li>    Customer mycustomer = <b><font color="#0000ff">new</font></b> Customer(); <br /></li><li>    mycustomer.setName(<font color="#ff33ff">"Velocity"</font>); <br /></li><li>    mycustomer.setAddress(<font color="#ff33ff">"jakarta.apache.org/velocity/index.html"</font>); <br /></li><li>    mycustomer.setAge(2); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目录</font></i><br /></li><li>    Velocity.init(<font color="#ff33ff">"D:\\Template\\resource\\jt.properties"</font>); <br /></li><li>    <i><font color="#339900">//模版文件名，模版文件所在的路径在上一条语句中已经设置了</font></i><br /></li><li>    Template template =Velocity.getTemplate(<font color="#ff33ff">"hello.vm"</font>, <font color="#ff33ff">"gb2312"</font>);  <br /></li><li>    <i><font color="#339900">//实例化一个Context</font></i><br /></li><li>    VelocityContext context = <b><font color="#0000ff">new</font></b> VelocityContext();     <br /></li><li>    <i><font color="#339900">//把模版变量的值设置到context中</font></i><br /></li><li>    context.put(<font color="#ff33ff">"Customer"</font>, mycustomer);     <br /></li><li>    <i><font color="#339900">//开始模版的替换</font></i><br /></li><li>    template.merge(context, writer); <br /></li><li></li><li>    <i><font color="#339900">//写到文件中</font></i><br /></li><li>    <font color="#ff0000">PrintWriter</font> filewriter = <b><font color="#0000ff">new</font></b><font color="#ff0000">PrintWriter</font>(<b><font color="#0000ff">new</font></b><font color="#ff0000">FileOutputStream</font>(outpath),<b><font color="#0000ff">true</font></b>); <br /></li><li>    filewriter.println(writer.toString()); <br /></li><li>    filewriter.close(); </li></ol></div><br />    输出结果：<br /><br /><div class="codeStyle"><ol><li></li><li>    &lt;<font color="#ff0000">HTML</font>&gt; <br /></li><li>        &lt;BODY&gt; <br /></li><li>            <font color="#ff0000">Name</font>:        Velocity <br /></li><li>            Address:    jakarta.apache.orgvelocityindex.html <br /></li><li>            Age:        2 <br /></li><li>        &lt;/BODY&gt; <br /></li><li>    &lt;/<font color="#ff0000">HTML</font>&gt; </li></ol></div><br />    除了替换变量之外，象Velocity高级引擎还能做其他许多事情，它们有用来比较和迭代的内建指令，通过这些指令我们可以完成程序语言中的条件判断语句和循环语句等<br />    <br />    例如，我们想要输出年龄等于2的所有客户的信息，我们可以这样定义我们的模版<br />    模版：<br /><br /><div class="codeStyle"><ol><li></li><li>    &lt;<font color="#ff0000">HTML</font>&gt; <br /></li><li>        &lt;BODY&gt; <br /></li><li>            &lt;table&gt; <br /></li><li>                &lt;tr&gt; <br /></li><li>                    &lt;td&gt;名称&lt;/td&gt;&lt;td&gt;地址&lt;/td&gt;&lt;td&gt;年龄&lt;/td&gt; <br /></li><li>                &lt;/tr&gt; <br /></li><li>                #foreach ($Customer in $allCustomer) <br /></li><li>                    #<b><font color="#0000ff">if</font></b>($Customer.Age()==<font color="#ff33ff">"2"</font>) <br /></li><li>                    &lt;tr&gt; <br /></li><li>                        &lt;td&gt;$Customer.<font color="#ff0000">Name</font>()&lt;/td&gt; <br /></li><li>                        &lt;td&gt;$Customer.Address()&lt;/td&gt; <br /></li><li>                        &lt;td&gt;$Customer.Age()&lt;/td&gt; <br /></li><li>                    &lt;/tr&gt; <br /></li><li>                    #end <br /></li><li>                #end <br /></li><li>            &lt;/table&gt; <br /></li><li>        &lt;/BODY&gt; <br /></li><li>    &lt;/<font color="#ff0000">HTML</font>&gt; </li></ol></div>    <br /><br />    java的代码：    <br /><br /><div class="codeStyle"><ol><li></li><li>    <i><font color="#339900">//设置客户信息</font></i><br /></li><li>    <font color="#ff0000">ArrayList</font> allMyCustomer = <b><font color="#0000ff">new</font></b><font color="#ff0000">ArrayList</font>(); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//客户1</font></i><br /></li><li>    Customer mycustomer1 = <b><font color="#0000ff">new</font></b> Customer(); <br /></li><li>    mycustomer1.setName(<font color="#ff33ff">"Velocity"</font>); <br /></li><li>    mycustomer1.setAddress(<font color="#ff33ff">"jakarta.apache.org/velocity/index.html"</font>); <br /></li><li>    mycustomer1.setAge(2); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//客户2</font></i><br /></li><li>    Customer mycustomer2 = <b><font color="#0000ff">new</font></b> Customer(); <br /></li><li>    mycustomer2.setName(<font color="#ff33ff">"Tomcat"</font>); <br /></li><li>    mycustomer2.setAddress(<font color="#ff33ff">"jakarta.apache.org/tomcat/index.html"</font>); <br /></li><li>    mycustomer2.setAge(3); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//客户3</font></i><br /></li><li>    Customer mycustomer3 = <b><font color="#0000ff">new</font></b> Customer(); <br /></li><li>    mycustomer3.setName(<font color="#ff33ff">"Log4J"</font>); <br /></li><li>    mycustomer3.setAddress(<font color="#ff33ff">"jakarta.apache.org/log4j/docs/index.html"</font>); <br /></li><li>    mycustomer3.setAge(2); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//添加到allMyCustomer(ArrayList)中.</font></i><br /></li><li>    allMyCustomer.add(mycustomer1); <br /></li><li>    allMyCustomer.add(mycustomer2); <br /></li><li>    allMyCustomer.add(mycustomer3); <br /></li><li>     <br /></li><li>    <i><font color="#339900">//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目录</font></i><br /></li><li>    Velocity.init(<font color="#ff33ff">"D:\\Template\\resource\\jt.properties"</font>); <br /></li><li>    <i><font color="#339900">//模版文件名，模版文件所在的路径在上一条语句中已经设置了</font></i><br /></li><li>    Template template =Velocity.getTemplate(<font color="#ff33ff">"hello.vm"</font>, <font color="#ff33ff">"gb2312"</font>);  <br /></li><li>    <i><font color="#339900">//实例化一个Context</font></i><br /></li><li>    VelocityContext context = <b><font color="#0000ff">new</font></b> VelocityContext();     <br /></li><li></li><li>    <i><font color="#339900">/**</font></i><br /></li><li><i><font color="#339900">     * 注意这里我们仅仅需要给一个模版变量负值</font></i><br /></li><li><i><font color="#339900">     */</font></i><br /></li><li>    context.put(<font color="#ff33ff">"allCustomer"</font>, allMyCustomer);     <br /></li><li>    <i><font color="#339900">//开始模版的替换</font></i><br /></li><li>    template.merge(context, writer); <br /></li><li></li><li>    <i><font color="#339900">//写到文件中</font></i><br /></li><li>    <font color="#ff0000">PrintWriter</font> filewriter = <b><font color="#0000ff">new</font></b><font color="#ff0000">PrintWriter</font>(<b><font color="#0000ff">new</font></b><font color="#ff0000">FileOutputStream</font>(outpath),<b><font color="#0000ff">true</font></b>); <br /></li><li>    filewriter.println(writer.toString()); <br /></li><li>    filewriter.close(); </li></ol></div>    <br />    结果：<br /><br /><div class="codeStyle"><ol><li></li><li>    &lt;<font color="#ff0000">HTML</font>&gt; <br /></li><li>        &lt;BODY&gt; <br /></li><li>            &lt;table&gt; <br /></li><li>                &lt;tr&gt; <br /></li><li>                    &lt;td&gt;名称&lt;/td&gt;&lt;td&gt;地址&lt;/td&gt;&lt;td&gt;年龄&lt;/td&gt; <br /></li><li>                &lt;/tr&gt; <br /></li><li>                &lt;tr&gt;                 <br /></li><li>                    &lt;td&gt;Velocity&lt;/td&gt; <br /></li><li>                    &lt;td&gt;jakarta.apache.orgvelocityindex.html&lt;/td&gt; <br /></li><li>                    &lt;td&gt;2&lt;/td&gt; <br /></li><li>                &lt;/tr&gt; <br /></li><li>                &lt;tr&gt;                 <br /></li><li>                    &lt;td&gt;Log4J&lt;/td&gt; <br /></li><li>                    &lt;td&gt;jakarta.apache.orglog4jdocsindex.html&lt;/td&gt; <br /></li><li>                    &lt;td&gt;2&lt;/td&gt; <br /></li><li>                &lt;/tr&gt; <br /></li><li>            &lt;/table&gt; <br /></li><li>        &lt;/BODY&gt; <br /></li><li>    &lt;/<font color="#ff0000">HTML</font>&gt; </li></ol></div><br />    #IF语句完成逻辑判断，这个我想不用多说了。<br />    allCustomer对象包含零个或者多个Customer对象。由于ArrayList(List,HashMap,HashTable,Iterator,Vector等)属于Java Collections Framework的一部分，我们可以用#foreach指令迭代其内容。我们不用担心如何定型对象的类型——映像引擎会为我们完成这个任务。#foreach指令的一般格式是“#foreach in ”。#foreach指令迭代list，把list中的每个元素放入item参数，然后解析#foreach块内的内容。对于list内的每个元素，#foreach块的内容都会重复解析一次。从效果上看，它相当于告诉模板引擎说：“把list中的每一个元素依次放入item变量，每次放入一个元素，输出一次#foreach块的内容”。<br />    <br />    MVC设计模型：<br />    使用模板引擎最大的好处在于，它分离了代码（或程序逻辑）和表现（输出）。由于这种分离，你可以修改程序逻辑而不必担心邮件消息本身；类似地，你（或公关部门的职员）可以在不重新编译程序的情况下，重新编写客户列表。 实际上，我们分离了系统的数据模式（Data Model，即提供数据的类）、控制器（Controller，即客户列表程序）以及视图（View，即模板）。这种三层体系称为Model-View-Controller模型（MVC）。如果遵从MVC模型，代码分成三个截然不同的层，简化了软件开发过程中所有相关人员的工作。 结合模板引擎使用的数据模式可以是任何Java对象，最好是使用Java Collection Framework的对象。控制器只要了解模板的环境（如VelocityContext），一般这种环境都很容易使用。一些关系数据库的“对象-关系”映射工具能够和模板引擎很好地协同，简化JDBC操作；对于EJB，情形也类似。 模板引擎与MVC中视图这一部分的关系更为密切。模板语言的功能很丰富、强大，足以处理所有必需的视图功能，同时它往往很简单，不熟悉编程的人也可以使用它。模板语言不仅使得设计者从过于复杂的编程环境中解脱出来，而且它保护了系统，避免了有意或无意带来危险的代码。例如，模板的编写者不可能编写出导致无限循环的代码，或侵占大量内存的代码。不要轻估这些安全机制的价值；大多数模板编写者不懂得编程，从长远来看，避免他们接触复杂的编程环境相当于节省了你自己的时间。 许多模板引擎的用户相信，在采用模板引擎的方案中，控制器部分和视图部分的明确分离，再加上模板引擎固有的安全机制，使得模板引擎足以成为其他内容发布系统（比如JSP）的替代方案。因此，Java模板引擎最常见的用途是替代JSP也就不足为奇了。 <br />    HTML处理 ：<br />    由于人们总是看重模板引擎用来替换JSP的作用，有时他们会忘记模板还有更广泛的用途。到目前为止，模板引擎最常见的用途是处理HTML Web内容。但我还用模板引擎生成过SQL、email、XML甚至Java源代码。<br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/49340.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-31 22:03 <a href="http://www.blogjava.net/TrampEagle/articles/49340.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DisplayTag应用</title><link>http://www.blogjava.net/TrampEagle/articles/48815.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 29 May 2006 10:24:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48815.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48815.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48815.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48815.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48815.html</trackback:ping><description><![CDATA[引自：<br />--  作者：bibiye<br />--  发布时间：2005-12-16 13:18:07<br /><br />--  DisplayTag应用<br />DisplayTag是一个非常好用的表格显示标签，适合MVC模式，其主页在<a href="http://displaytag.sourceforge.net/" target="_blank"><font color="#000000">http://displaytag.sourceforge.net</font></a>  <br /><b>一、最简单的情况，未使用&lt;display:column/&gt;标签<br /></b>  &lt;%request.setAttribute( "test", new ReportList(6) );%&gt;<br />  &lt;display:table name="test" /&gt;<br />  标签遍历List里的每一个对象，并将对象里的所有属性显示出来。一般用于开发的时候检查对象数据的完整性。<br />  <br /><b>二、使用&lt;display:column/&gt;标签的情况</b><br />&lt;display:table name="test"&gt;<br />  &lt;display:column property="id" title="ID" /&gt;<br />  &lt;display:column property="name" /&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" /&gt;<br />  &lt;display:column property="description" title="Comments"/&gt;<br />&lt;/display:table&gt;<br />   property对应List里对象的属性（用getXXX()方法取得），title则对应表格表头里的列名。定义列有两种方式：<br />   A、&lt;display:column property="email" /&gt; <br />      使用&lt;display:column/&gt;标签里的property属性来定义<br />   B、&lt;display:column title="email"&gt;email@it.com&lt;/display:column&gt; <br />      在&lt;display:column/&gt;标签体里增加内容，可以是常量，也可以用其他标签等等<br />   两种方式比较，用property属性来定义更加快速和利于排序。<br />   <br /><b>三、表格显示样式的定义<br /></b>  A、在&lt;display:table/&gt;和&lt;display:column/&gt;标签里指定标准的html属性，烦琐<br />  B、修改样式表<br />&lt;display:table name="test" class="mars"&gt;<br />  &lt;display:column property="id" title="ID" class="idcol"/&gt;<br />  &lt;display:column property="name" /&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" class="tableCellError" /&gt;<br />  &lt;display:column property="description" title="Comments"/&gt;<br />&lt;/display:table&gt;<br />   通过class属性来指定所要应用的样式。可以在其默认样式表里（./css/screen.css）直接修改<br />   <br /><b>四、标签取得数据的数据源<br /></b>  有四种范围<br />   pageScope <br />   requestScope (默认)  &lt;display:table name="test2" &gt;<br />   sessionScope  &lt;display:table name="sessionScope.holder.list" &gt; 注意，这里要指定范围，非默认<br />   applicationScope <br />   <br /><b>五、通过增加id属性创建隐含的对象<br /></b>&lt;display:table name="test" id="testit"&gt;<br />    &lt;display:column property="id" title="ID" /&gt;<br />    &lt;display:column property="name" /&gt;<br />    &lt;display:column title="static value"&gt;static&lt;/display:column&gt;<br />    &lt;display:column title="row number (testit_rowNum)"&gt;&lt;%=pageContext.getAttribute("testit_rowNum")%&gt;&lt;/display:column&gt;<br />    &lt;display:column title="((ListObject)testit).getMoney()"&gt;&lt;%=((ListObject)pageContext.getAttribute("testit")).getMoney()%&gt;&lt;/display:column&gt;<br />&lt;/display:table&gt;<br />   注意到在&lt;display:table/&gt;里增加了id属性，这时就在page context里创建了一个隐含对象，指向List里的当前对象，<br />   可以通过(ListObject)pageContext.getAttribute("id")来捕获这个对象。同时还创建了一个id_rowNum对象，同样，可<br />   通过pageContext.getAttribute("testit_rowNum")来捕获，它仅仅代表当前行的行数。<br />   有了这两个隐含对象，就可以通过其他标签来访问，例如Jstl:<br />  &lt;display:table id="row" name="mylist"&gt;<br />    &lt;display:column title="row number" &gt;<br />      &lt;c:out value="${row_rowNum}"/&gt;<br />    &lt;/display:column&gt;<br />    &lt;display:column title="name" &gt;<br />      &lt;c:out value="${row.first_name}"/&gt;<br />      &lt;c:out value="${row.last_name}"/&gt;<br />    &lt;/display:column&gt;<br />  &lt;/display:table&gt;<br />  <br /><b>六、显示部分数据</b><br />   显示开始五条数据：通过设定length属性<br />&lt;display:table name="test" length="5"&gt;<br />  &lt;display:column property="id" title="ID" /&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" /&gt;<br />&lt;/display:table&gt;<br />   显示第三到第八条数据：通过设定offset和length属性<br />&lt;display:table name="test" offset="3" length="5"&gt;<br />  &lt;display:column property="id" title="ID" /&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" /&gt;<br />&lt;/display:table&gt;  <br /><p><b>七、对email和url地址的直接连接</b><br />&lt;display:table name="test" &gt;<br />  &lt;display:column property="id" title="ID" /&gt;<br />  &lt;display:column property="email" autolink="true" /&gt;<br />  &lt;display:column property="url" autolink="true" /&gt;<br />&lt;/display:table&gt;<br />如果要显示的对象里包含email和url地址，则可以在display:column里直接设定autolink="true"来直接连接<br /><br /><b>八、使用装饰模式转换数据显示（写自己的 decorator ）</b><br />  A、对整个表格应用decorator<br />  &lt;display:table name="test" decorator="org.displaytag.sample.Wrapper" &gt;<br />      &lt;display:column property="id" title="ID" /&gt;<br />      &lt;display:column property="email" /&gt;<br />      &lt;display:column property="status" /&gt;<br />      &lt;display:column property="date" /&gt;<br />      &lt;display:column property="money" /&gt;<br />  &lt;/display:table&gt;<br />    org.displaytag.sample.Wrapper即自己写的decorator，它要继承TableDecorator类，看看它的一个方法：<br />        public String getMoney()<br />    {<br />        return this.moneyFormat.format(((ListObject) this.getCurrentRowObject()).getMoney());<br />    }<br />    很明显，它通过父类的getCurrentRowObject()方法获得当前对象，然后对其getMoney()方法进行‘油漆’<br />  B、对单独的column应用decorator<br />  &lt;display:table name="test"&gt;<br />     &lt;display:column property="id" title="ID" /&gt;<br />     &lt;display:column property="email" /&gt;<br />     &lt;display:column property="status" /&gt;<br />     &lt;display:column property="date" decorator="org.displaytag.sample.LongDateWrapper" /&gt;<br />  &lt;/display:table&gt;<br />    org.displaytag.sample.LongDateWrapper要实现ColumnDecorator接口，它的方法：<br />        public final String decorate(Object columnValue)<br />    {<br />        Date date = (Date) columnValue;<br />        return this.dateFormat.format(date);<br />    }<br />    显然，它获得不了当前对象（因为它实现的是接口），仅仅是获得该对象的columnValue，然后‘油漆’<br />    <br /><b>九、创建动态连接<br /></b>   有两种方法创建动态连接：<br />   A、在&lt;display:column/&gt;里通过增加href、paramId、paramName、paramScope、paramProperty属性<br />      href             基本的URL 地址<br />      paramId          加在URL 地址后的参数名称<br />      paramName        数据bean的名称，一般为null（即使用当前List里的对象）<br />      paramScope       数据bean的范围，一般为null<br />      paramProperty    数据bean的属性名称，用来填充URL 地址后的参数值<br />&lt;display:table name="sessionScope.details"&gt;<br />  &lt;display:column property="id" title="ID" href="details.jsp" paramId="id" /&gt;<br />  &lt;display:column property="email" href="details.jsp" paramId="action" paramName="testparam" paramScope="request" /&gt;<br />  &lt;display:column property="status" href="details.jsp" paramId="id" paramProperty="id" /&gt;<br />&lt;/display:table&gt;  <br />    这种方法简便直接，但缺点是无法产生类似details.jsp?id=xx&amp;action=xx的复合URL<br />   B、应用decorator 创建动态连接：<br />&lt;display:table name="sessionScope.details" decorator="org.displaytag.sample.Wrapper" &gt;<br />  &lt;display:column property="link1" title="ID" /&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="link2" title="Actions" /&gt;<br />&lt;/display:table&gt;<br />   org.displaytag.sample.Wrapper里的方法：<br />public String getLink1()<br />{<br />  ListObject lObject= (ListObject)getCurrentRowObject();<br />  int lIndex= getListIndex();<br />  return "&lt;a href=\\"details.jsp?index=" + lIndex + "\\"&gt;" + lObject.getId() + "&lt;/a&gt;";<br />}</p><p><br />public String getLink2()<br />{<br />  ListObject lObject= (ListObject)getCurrentRowObject();<br />  int lId= lObject.getId();</p><p>  return "&lt;a href=\\"details.jsp?id=" + lId<br />   + "&amp;action=view\\"&gt;View&lt;/a&gt; | "<br />   + "&lt;a href=\\"details.jsp?id=" + lId<br />   + "&amp;action=edit\\"&gt;Edit&lt;/a&gt; | "<br />   + "&lt;a href=\\"details.jsp?id=" + lId<br />   + "&amp;action=delete\\"&gt;Delete&lt;/a&gt;";<br />}</p><p><b>十、分页<br /></b>   实现分页非常的简单，增加一个pagesize属性指定一次想显示的行数即可<br />&lt;display:table name="sessionScope.test" pagesize="10"&gt;<br />&lt;display:column property="id" title="ID" /&gt;<br />&lt;display:column property="name" /&gt;<br />&lt;display:column property="email" /&gt;<br />&lt;display:column property="status" /&gt;<br />&lt;/display:table&gt;</p><p><b>十一、排序<br /></b>   排序实现也是很简单，在需要排序的column里增加sortable="true"属性，headerClass="sortable"仅仅是<br />   指定显示的样式。column里的属性对象要实现Comparable接口，如果没有的话可以应用decorator<br />   defaultsort="1"              默认第一个column排序<br />   defaultorder="descending"    默认递减排序<br />&lt;display:table name="sessionScope.stest" defaultsort="1" defaultorder="descending"&gt;<br />  &lt;display:column property="id" title="ID" sortable="true" headerClass="sortable" /&gt;<br />  &lt;display:column property="name" sortable="true" headerClass="sortable"/&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" sortable="true" headerClass="sortable"/&gt;<br />&lt;/display:table&gt;<br />  注意的是，当同时存在分页时排序仅仅针对的是当前页面，而不是整个List都进行排序<br />  <br /><b>十二、column 分组<br /></b>   分组只是需要在column里增加group属性<br />&lt;display:table name="test" class="simple"&gt;<br />  &lt;display:column property="city" title="CITY" group="1"/&gt;<br />  &lt;display:column property="project" title="PROJECT" group="2"/&gt;<br />  &lt;display:column property="amount" title="HOURS"/&gt;<br />  &lt;display:column property="task" title="TASK"/&gt;<br />&lt;/display:table&gt;</p><p><b>十三、导出数据到其他格式（页面溢出filter??）</b><br />   在&lt;display:table/&gt;里设定export="true"<br />   在&lt;display:column/&gt;里设定media="csv excel xml pdf" 决定该字段在导出到其他格式时被包不包含，不设定则都包含<br />   &lt;display:setProperty name="export.csv" value="false" /&gt;<br />   决定该种格式能不能在页面中导出<br />&lt;display:table name="test" export="true" id="currentRowObject"&gt;<br />  &lt;display:column property="id" title="ID"/&gt;<br />  &lt;display:column property="email" /&gt;<br />  &lt;display:column property="status" /&gt;<br />  &lt;display:column property="longDescription" media="csv excel xml pdf" title="Not On HTML"/&gt;<br />  &lt;display:column media="csv excel" title="URL" property="url"/&gt;<br />  &lt;display:setProperty name="export.pdf" value="true" /&gt;<br />  &lt;display:setProperty name="export.csv" value="false" /&gt;<br />&lt;/display:table&gt;</p><p><b>十四、配置属性，覆盖默认<br /></b>  两种方法：<br />  A、在程序classpath下新建displaytag.properties文件<br />  B、对于单个表格，应用&lt;display:setProperty&gt;标签<br />  具体可配置的属性：<a href="http://displaytag.sourceforge.net/configuration.html" target="_blank"><font color="#000000">http://displaytag.sourceforge.net/configuration.html</font></a><br />  <br /><b>十五、一个完整的例子</b><br />&lt;display:table name="test" export="true" sort="list" pagesize="8"&gt;<br />  &lt;display:column property="city" title="CITY" group="1" sortable="true"    headerClass="sortable"/&gt;<br />  &lt;display:column property="project" title="PROJECT" group="2" sortable="true" headerClass="sortable"/&gt;<br />  &lt;display:column property="amount" title="HOURS"/&gt;<br />  &lt;display:column property="task" title="TASK"/&gt;<br />&lt;/display:table&gt; <br />   sort="list" 对整个list进行排序<br />   导出数据到其他格式时，group无效<br /><br /><a href="http://blog.donews.com/lveyo/archive/2006/04/27/848346.aspx">解决DisplayTag有中文条件时的分页问题 </a>(转自：<a href="http://blog.donews.com/lveyo/archive/2006/04/27/848346.aspx">http://blog.donews.com/lveyo/archive/2006/04/27/848346.aspx</a>)<br /><br /><br /></p><p>很早就开始用DisplayTag了，真得很方便，不用考虑分页，表格等问题。</p><p>但是有一个问题一直困扰我，就是有中文作为表单提交内容的时候，翻页就不能用了，中文变成了乱码。</p><p>表单传送时，<!--StartFragment --> form的提交采用的是Post方法，这没错，但链接采用的是Get方法，问题就出在这里，因为和Tomcat4相比，Tomcat5的post和get采用的方式不再相同了，汉字编码的实现方式也不同了。</p><p>Post是将地址传送一次，将form的数据单独提交，而Get则是将地址和参数一起传送，传送的不止是form的数据。<br /><br /><!--StartFragment --> <a>默认情况下，Tomcat对请求采用的默认编码是ISO-8859-1，</a><a>这样我们提交的汉字被认为是ISO-8859-1的编码，所以在程序中接收时显示乱码</a>。 <!--StartFragment --> 在过滤器中调用request.setCharacterEncoding("GBK")，那么Post上来的汉字将被认为是GBK编码，而Tomcat5对于Get请求上来的编码并不根据过滤器的设定辨认编码方式，默认的依然是ISO-8859-1，<a>所以，即使你的页面使用默认的编码方式进行编码，然后使用ISO-8859-1进行解码，得到的结果也不对</a>。 </p><p>解决方法：<br /><a>在Server.xml的connector部分设定URIEncoding的值，根据编码方式指定自己的值</a>就可以了<br />例如：URIEncoding="GB2312"</p><img src ="http://www.blogjava.net/TrampEagle/aggbug/48815.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-29 18:24 <a href="http://www.blogjava.net/TrampEagle/articles/48815.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EasyJWeb－Velocity脚本教程</title><link>http://www.blogjava.net/TrampEagle/articles/39873.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 07 Apr 2006 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/39873.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/39873.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/39873.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/39873.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/39873.html</trackback:ping><description><![CDATA[
		<p>引自：<a href="http://www.easyjf.com/easyjweb/velocity.htm">http://www.easyjf.com/easyjweb/velocity.htm</a><br /><br />Velocity是一个基于java的模板引擎（template engine）。它允许任何人仅仅简单的使用模板语言（template language）来引用由java代码定义的对象。 当Velocity应用于web开发时，界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点，也就是说，页面设计人员可以只关注页面的显示效果，而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来，这样为web站点的长期维护提供了便利，同时也为我们在JSP和PHP之外又提供了一种可选的方案。 </p>
		<p>官方网站：<a class="style1" href="http://jakarta.apache.org/velocity/" target="_blank">http://jakarta.apache.org/velocity/</a><br /><br /></p>
		<p>
				<font color="#003399">
						<strong>Velocity脚本摘要<br /></strong>
						<font color="#000000">1、声明:#set ($var=XXX)<br />左边可以是以下的内容<br />Variable reference <br />String literal <br />Property reference <br />Method reference <br />Number literal #set ($i=1) <br />ArrayList #set ($arr=["yt1","t2"])<br />技持算术运算符<br /></font>
				</font>
		</p>
		<p>
				<strong>2、注释:<br />单行## XXX<br />多行#* xxx<br />xxxx<br />xxxxxxxxxxxx*#</strong>
		</p>
		<p>
				<strong>References 引用的类型<br />3、变量 Variables <br />以 "$" 开头，第一个字符必须为字母。character followed by a VTL Identifier. (a .. z <br />or A .. Z).<br />变量可以包含的字符有以下内容：<br />alphabetic (a .. z, A .. Z) <br />numeric (0 .. 9) <br />hyphen ("-") <br />underscore ("_") </strong>
		</p>
		<p>
				<strong>4、Properties <br />$Identifier.Identifier<br />$user.name<br />hashtable user中的的name值.类似：user.get("name")</strong>
		</p>
		<p>
				<strong>5、Methods <br />object user.getName() = $user.getName()</strong>
		</p>
		<p>
				<strong>6、Formal Reference Notation <br />用{}把变量名跟字符串分开 </strong>
		</p>
		<p>
				<strong>如<br />#set ($user="csy"}<br />${user}name <br />返回csyname<br /></strong>
		</p>
		<p>
				<strong>$username<br />$!username<br />$与$!的区别<br />当找不到username的时候，$username返回字符串"$username"，而$!username返回空字符串"" </strong>
		</p>
		<p>
				<strong>7、双引号 与 引号 <br />#set ($var="helo")<br />test"$var" 返回testhello<br />test'$var' 返回test'$var'<br />可以通过设置 stringliterals.interpolate=false改变默认处理方式<br /></strong>
		</p>
		<p>
				<strong>8、条件语句<br />#if( $foo ) <br />&lt;strong&gt;Velocity!&lt;/strong&gt;<br />#end<br />#if($foo)<br />#elseif()<br />#else<br />#end<br />当$foo为null或为Boolean对象的false值执行.</strong>
		</p>
		<p>
				<strong>9、逻辑运算符:== &amp;&amp; || !</strong>
		</p>
		<p>
				<strong>10、循环语句#foreach($var in $arrays ) // 集合包含下面三种Vector, a Hashtable or an Array<br />#end<br />#foreach( $product in $allProducts )<br />&lt;li&gt;$product&lt;/li&gt;<br />#end</strong>
		</p>
		<p>
				<strong>#foreach( $key in $allProducts.keySet() )<br />&lt;li&gt;Key: $key -&gt; Value: $allProducts.get($key)&lt;/li&gt;<br />#end</strong>
		</p>
		<p>
				<strong>#foreach( $customer in $customerList )<br />&lt;tr&gt;&lt;td&gt;$velocityCount&lt;/td&gt;&lt;td&gt;$customer.Name&lt;/td&gt;&lt;/tr&gt;<br />#end<br />11、velocityCount变量在配置文件中定义<br /># Default name of the loop counter<br /># variable reference.<br />directive.foreach.counter.name = velocityCount<br /># Default starting value of the loop<br /># counter variable reference.<br />directive.foreach.counter.initial.value = 1<br /></strong>
		</p>
		<p>
				<strong>12、包含文件 <br />#include( "one.gif","two.txt","three.htm" )<br /><br />13、Parse导入脚本<br />#parse("me.vm" )</strong>
		</p>
		<p>
				<strong>14、#stop 停止执行并返回 </strong>
		</p>
		<p>
				<strong>15、定义宏Velocimacros ,相当于函数 支持包含功能<br />#macro( d )<br />&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;<br />#end<br />调用 <br />#d()</strong>
		</p>
		<p>
				<strong>16、带参数的宏<br />#macro( tablerows $color $somelist )<br />#foreach( $something in $somelist )<br />&lt;tr&gt;&lt;td bgcolor=$color&gt;$something&lt;/td&gt;&lt;/tr&gt;<br />#end<br />#end<br /><br />17、Range Operator <br />#foreach( $foo in [1..5] )<br /></strong>
		</p>
		<p>
				<font color="#003399">
						<strong>Velocity用户手册---中文版 (来源网络) <br /></strong>
				</font>
		</p>
		<div align="left">
				<p align="left">Velocity是什么？ </p>
				<p align="left">Velocity是一个基于java的模板引擎（template engine）。它允许任何人仅仅简单的使用模板语言（template language）来引用由java代码定义的对象。 </p>
				<p align="left">当Velocity应用于web开发时，界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点，也就是说，页面设计人员可以只关注页面的显示效果，而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来，这样为web站点的长期维护提供了便利，同时也为我们在JSP和PHP之外又提供了一种可选的方案。 </p>
				<p align="left">Velocity的能力远不止web站点开发这个领域，例如，它可以从模板（template）产生SQL和PostScript、XML，它也可以被当作一个独立工具来产生源代码和报告，或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务（template service）。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。 </p>
				<p align="left">Velocity能为我们作什么？ </p>
				<p align="left">The Mud Store Example </p>
				<p align="left">假设你是一家专门出售Mud的在线商店的页面设计人员，让我们暂且称它为“在线MUD商店”。你们的业务很旺，客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站，登陆后就允许他们查看订单并购买更多的mud。现在，一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud，由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的，所以某天有一个问题可能会冒出来：为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢？ </p>
				<p align="left">Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员，你希望每个用户登陆时都拥有自己的页面。 </p>
				<p align="left">你会见了一些公司内的软件工程师，你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边，看一看你应该作些什么吧。 </p>
				<p align="left">你可能在页面内嵌套如下的VTL声明： </p>
				<p align="left">&lt;html&gt; </p>
				<p align="left">&lt;body&gt; </p>
				<p align="left">Hello $customer.Name! </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">#foreach( $mud in $nudsOnSpecial ) </p>
				<p align="left">#if ( $customer.hasPurchased( $mud ) ) </p>
				<p align="left">&lt;tr&gt;&lt;td&gt;$flogger.getPromo( $mud )&lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">#end </p>
				<p align="left">#end </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">Velocity Template Language(VTL):AN introduction </p>
				<p align="left">VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。 </p>
				<p align="left">VTL使用references来在web site内嵌套动态内容，一个变量就是一种类型的reference。变量是某种类型的refreence，它可以指向java代码中的定义，或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子，它可以被嵌套到HTML代码中： </p>
				<p align="left">#set ( $a = “Velocity” ) </p>
				<p align="left">和所有的VTL statement一样，这个statement以＃字符开始并且包含一个directive：set。当一个在线用户请求你的页面时，Velocity Templating Engine将查询整个页面以便发现所有＃字符，然后确定哪些是VTL statement，哪些不需要VTL作任何事情。 </p>
				<p align="left">＃字符后紧跟一个directive：set时，这个set directive使用一个表达式（使用括号封闭）――一个方程式分配一个值给变量。变量被列在左边，而它的值被列在右边，最后他们之间使用＝号分割。 </p>
				<p align="left">在上面的例子中，变量是$a，而它的值是Velocity。和其他的references一样以$字符开始，而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。 </p>
				<p align="left">记住以下的规则： </p>
				<p align="left">使用$字符开始的references用于得到什么；使用#字符开始的directives用于作些什么。 </p>
				<p align="left">Hello Velocity World! </p>
				<p align="left">一旦某个变量被分配了一个值，那么你就可以在HTML文件的任何地方引用它。在下面的例子中，一个值被分配给$foo变量，并在其后被引用。 </p>
				<p align="left">&lt;html&gt; </p>
				<p align="left">&lt;body&gt; </p>
				<p align="left">#set ( $foo = “Velocity” ) </p>
				<p align="left">Hello $foo World! </p>
				<p align="left">&lt;/body&gt; </p>
				<p align="left">&lt;/html&gt; </p>
				<p align="left">上面的实现结果是在页面上打印“Hello Velocity World！” </p>
				<p align="left">为了使包含VTL directives的statement更具有可读性，我们鼓励你在新行开始每个VTL statement，尽管你不是必须这么作。Set directive将在后面详细描述。 </p>
				<p align="left">注释 </p>
				<p align="left">单行注释： </p>
				<p align="left">## This is a single line comment. </p>
				<p align="left">多行注释： </p>
				<p align="left">#* </p>
				<p align="left">Thus begins a multi-line comment. Online visitors won't </p>
				<p align="left">see this text because the Velocity Templating Engine will </p>
				<p align="left">ignore it. </p>
				<p align="left">*# </p>
				<p align="left">文档格式： </p>
				<p align="left">#** </p>
				<p align="left">This is a VTL comment block and </p>
				<p align="left">may be used to store such information </p>
				<p align="left">as the document author and versioning </p>
				<p align="left">information: </p>
				<p align="left">@version 5 </p>
				<p align="left">@author </p>
				<p align="left">*# </p>
				<p align="left">References </p>
				<p align="left">在VTL中有三种类型的references：变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者，你和你的工程师必须就references的名称达成共识，以便你可以在你的template中使用它们。 </p>
				<p align="left">Everything coming to and from a reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象，那么Velocity将调用它的toString()方法将这个对象转型为String类型。 </p>
				<p align="left">变量 </p>
				<p align="left">格式要求同java。 </p>
				<p align="left">属性 </p>
				<p align="left">例子： </p>
				<p align="left">$customer.Address </p>
				<p align="left">$purchase.Total </p>
				<p align="left">$customer.Address有两种含义。它可以表示：查找hashtable对象customer中以Address为关键字的值；也可以表示调用customer对象的getAddress()方法。当你的页面被请求时，Velocity将确定以上两种方式选用那种，然后返回适当的值。 </p>
				<p align="left">方法 </p>
				<p align="left">一个方法就是被定义在java中的一段代码，并且它有完成某些有用工作的能力，例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References，一般还包括一个VTL方法体。例如： </p>
				<p align="left">$customer.getAddress() </p>
				<p align="left">$purchase.getTotal() </p>
				<p align="left">$page.setTitle( “My Home Page” ) </p>
				<p align="left">$person.setAttributes( [“Strange”, “Weird”, “Excited”] ) </p>
				<p align="left">前两个例子$customer.getAddress()和$purchase.getTotal()看起来挺想上面的属性$customer.Address 和 $purchase.Total。如果你觉得他们之间有某种联系的话，那你是正确的。 </p>
				<p align="left">VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。 </p>
				<p align="left">正式reference标记 </p>
				<p align="left">reference的正是格式如下： </p>
				<p align="left">${mudSlinger}变量 </p>
				<p align="left">${customer.Address}属性 </p>
				<p align="left">${purchase.getTotal()}方法 </p>
				<p align="left">非正是格式更见常用，但是有时还是使用正是格式比较适合。例如：你希望通过一个变量$vice来动态的组织一个字符串。 </p>
				<p align="left">Jack is a $vicemaniac. </p>
				<p align="left">本来变量是$vice现在却变成了$vicemaniac，这样Veloctiy就不知道您到底要什么了。所以，应该使用正是格式书写 </p>
				<p align="left">Jack is a ${vice}maniac </p>
				<p align="left">现在Velocity知道变量是$vice而不是$vicemaniac。 </p>
				<p align="left">Quiet reference notation </p>
				<p align="left">例如： </p>
				<p align="left">&lt;input type=”text” name=”email” value=”$email” /&gt; </p>
				<p align="left">当页面的form被初始加载时，变量$email还没有值，这时你肯定是希望它能够显示一个blank text来代替输出”$email”这样的字段。那么使用quiet reference notation就比较合适。 </p>
				<p align="left">&lt;input type=”text” name=”email” value=”$!email”/&gt; </p>
				<p align="left">这样文本框的初始值就不会是email而是空值了。 </p>
				<p align="left">正式和quiet格式的reference notation也可一同使用，像下面这样： </p>
				<p align="left">&lt;input type=”text” name=”email” value=”$!{email}”/&gt; </p>
				<p align="left">Getting literal </p>
				<p align="left">Velocity使用特殊字符$和#来帮助它工作，所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。 </p>
				<p align="left">货币字符 </p>
				<p align="left">在VTL中使用$2.5这样的货币标识是没有问题得的，VTL不会将它错认为是一个reference，因为VTL中的reference总是以一个大写或者小写的字母开始。 </p>
				<p align="left">Escaping valid VTL reference </p>
				<p align="left">VTL中使用“\\”作为逃逸符。 </p>
				<p align="left">例如： </p>
				<p align="left">#set( $email = “foo” ) </p>
				<p align="left">$email </p>
				<p align="left">\\$email </p>
				<p align="left">\\\\$email </p>
				<p align="left">\\\\\\$email </p>
				<p align="left">将render为： </p>
				<p align="left">foo </p>
				<p align="left">$email </p>
				<p align="left">\\foo </p>
				<p align="left">\\\\$email </p>
				<p align="left">如果email变量没有被定义则 </p>
				<p align="left">$email </p>
				<p align="left">\\$email </p>
				<p align="left">\\\\$email </p>
				<p align="left">\\\\\\$email </p>
				<p align="left">将被render为： </p>
				<p align="left">$email </p>
				<p align="left">\\$email </p>
				<p align="left">\\\\$email </p>
				<p align="left">\\\\\\$email </p>
				<p align="left">注意：VTL中未被定义的变量将被认为是一个字符串，所以以下例子： </p>
				<p align="left">#set( $foo = “gibbous” ) </p>
				<p align="left">$moon = $foo </p>
				<p align="left">的输出结果是： </p>
				<p align="left">$moon = gibbous </p>
				<p align="left">Case substitution </p>
				<p align="left">现在你已经对reference比较熟悉了，你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如： </p>
				<p align="left">$foo </p>
				<p align="left">$foo.getBar() </p>
				<p align="left">## is the same as </p>
				<p align="left">$foo.Bar </p>
				<p align="left">$data.getUser(“jon”) </p>
				<p align="left">## is the same as </p>
				<p align="left">$data.User(“jon”) </p>
				<p align="left">$data.getRequest().getServerName() </p>
				<p align="left"># is the same as </p>
				<p align="left">$data.Request.ServerName </p>
				<p align="left">## is the same as </p>
				<p align="left">${data.Request.ServerName} </p>
				<p align="left">但是，注意VTL中不会将reference解释为对象的实例变量。例如：$foo.Name将被解释为Foo对象的getName（）方法，而不是Foo对象的Name实例变量。 </p>
				<p align="left">Directives </p>
				<p align="left">Reference允许设计者使用动态的内容，而directive使得你可以应用java代码来控制你的显示逻辑，从而达到你所期望的显示效果。 </p>
				<p align="left">#set </p>
				<p align="left">#set directive被用于设置一个reference的值。例如： </p>
				<p align="left">#set ( $primate = “monkey” ) </p>
				<p align="left">#set ( $customer.Behavior = $primate ) </p>
				<p align="left">赋值左侧的（LHS）必须是一个变量或者属性reference。右侧（RHS）可以是以下类型中一种： </p>
				<p align="left">l变量reference </p>
				<p align="left">lString literal </p>
				<p align="left">l属性reference </p>
				<p align="left">l方法reference </p>
				<p align="left">lnumber literal </p>
				<p align="left">lArrayList </p>
				<p align="left">下面是应用各种类型的RHS的例子： </p>
				<p align="left">＃set ( $monkey = $bill ) ##变量reference </p>
				<p align="left">＃set ( $monkey.Friend = “monica” ) ##String literal </p>
				<p align="left">＃set ( $monkey.Blame = $whitehouse.Leak )##属性reference </p>
				<p align="left">＃set ( $monkey.Plan = $spindoctor.weave($web) )##方法reference </p>
				<p align="left">＃set ( $monkey.Number = 123 )##Number literal </p>
				<p align="left">＃set ( $monkey.Say = [“Not”, $my, “fault”] )##ArrayList </p>
				<p align="left">注意：最后一个例子的取值方法为：$monkey.Say.get(0) </p>
				<p align="left">RHS也可以是一个简单的算术表达式： </p>
				<p align="left">#set ( $value = $foo + 1 ) </p>
				<p align="left">#set ( $value = $bar -1 ) </p>
				<p align="left">#set ( $value = $foo * $bar ) </p>
				<p align="left">#set ( $value = $foo / $bar ) </p>
				<p align="left">如果你的RHS是一个null，VTL的处理将比较特殊：它将指向一个已经存在的reference，这对初学者来讲可能是比较费解的。例如： </p>
				<p align="left">#set ( $resut = $query.criteria(“name”) ) </p>
				<p align="left">The result of the first query is $result </p>
				<p align="left">#set ( $resut = $query.criteria(“address”) ) </p>
				<p align="left">The result of the second query is $result </p>
				<p align="left">如果$query.criteria(“name”)返回一个“bill”，而$query.criteria(“address”)返回的是null，则显示的结果如下： </p>
				<p align="left">The result of the first query is bill </p>
				<p align="left">The result of the first query is bill </p>
				<p align="left">看看下面的例子： </p>
				<p align="left">#set( $criteria = ["name", "address"] ) </p>
				<p align="left">#foreach( $criterion in $criteria ) </p>
				<p align="left">#set( $result = $query.criteria($criterion) ) </p>
				<p align="left">#if( $result ) </p>
				<p align="left">Query was successful </p>
				<p align="left">#end </p>
				<p align="left">#end </p>
				<p align="left">在上面的例子中，程序将不能智能的根据$result的值决定查询是否成功。在$result被#set后（added to the context），它不能被设置回null（removed from the context）。打印的结果将显示两次查询结果都成功了，但是实际上有一个查询是失败的。 </p>
				<p align="left">为了解决以上问题我们可以通过预先定义的方式： </p>
				<p align="left">#set( $criteria = [“name”, “address”] ) </p>
				<p align="left">#foreach( $criterion in $criteria ) </p>
				<p align="left">#set( $result = false ) </p>
				<p align="left">#set( $result = $query.criteria( $criterion ) ) </p>
				<p align="left">#if( $result ) </p>
				<p align="left">Query was successful </p>
				<p align="left">#end </p>
				<p align="left">#end </p>
				<p align="left">String Literals </p>
				<p align="left">当你使用#set directive，String literal封闭在一对双引号内。 </p>
				<p align="left">#set ( $directoryRoot = “www” ) </p>
				<p align="left">#set ( $templateName = “index.vm” ) </p>
				<p align="left">#set ( $template = “$directoryRoot/$tempateName” ) </p>
				<p align="left">$template </p>
				<p align="left">上面这段代码的输出结果为：www/index.vm </p>
				<p align="left">但是，当string literal被封装在单引号内时，它将不被解析： </p>
				<p align="left">#set ( $foo = “bar” ) </p>
				<p align="left">$foo </p>
				<p align="left">#set ( $blargh = ‘$foo' ) </p>
				<p align="left">结果： </p>
				<p align="left">bar </p>
				<p align="left">$foo </p>
				<p align="left">上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。 </p>
				<p align="left">条件语句 </p>
				<p align="left">if/elseif/else </p>
				<p align="left">当一个web页面被生成时使用Velocity的#if directrive，如果条件成立的话可以在页面内嵌入文字。例如： </p>
				<p align="left">#if ( $foo ) </p>
				<p align="left">&lt;strong&gt;Velocity!&lt;/strong&gt; </p>
				<p align="left">#end </p>
				<p align="left">上例中的条件语句将在以下两种条件下成立： </p>
				<p align="left">l$foo是一个boolean型的变量，且它的值为true </p>
				<p align="left">l$foo变量的值不为null </p>
				<p align="left">这里需要注意一点：Velocity context仅仅能够包含对象，所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值，Velocity也会利用内省机制将它转换为一个Boolean的相同值。 </p>
				<p align="left">如果条件成立，那么#if和#end之间的内容将被显示。 </p>
				<p align="left">#elseif和#else元素可以同#if一同使用。例如： </p>
				<p align="left">#if( $foo &lt; 10 ) </p>
				<p align="left">&lt;strong&gt; Go North &lt;/strong&gt; </p>
				<p align="left">#elseif( $foo == 10 ) </p>
				<p align="left">&lt;strong&gt; Go East &lt;/strong&gt; </p>
				<p align="left">#elseif( $foo == 6 ) </p>
				<p align="left">&lt;strong&gt; Go South &lt;/strong&gt; </p>
				<p align="left">#else </p>
				<p align="left">&lt;strong&gt; Go West &lt;/strong&gt; </p>
				<p align="left">#end </p>
				<p align="left">注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false，但是与java不同它使用“＝＝”来比较两个值，而且velocity要求等号两边的值类型相同。 </p>
				<p align="left">关系、逻辑运算符 </p>
				<p align="left">Velocity中使用等号操作符判断两个变量的关系。例如： </p>
				<p align="left">#set ( $foo = “deoxyribonucleic acid” ) </p>
				<p align="left">#set ( $bar = “ribonucleic acid” ) </p>
				<p align="left">#if ( $foo == $foo ) </p>
				<p align="left">In this case it's clear they aren't equivalent.So… </p>
				<p align="left">#else </p>
				<p align="left">They are not equivalent and this will be the output. </p>
				<p align="left">#end </p>
				<p align="left">Velocity有AND、OR和NOT逻辑运算符。下面是一些例子： </p>
				<p align="left">## logical AND </p>
				<p align="left">#if( $foo &amp;&amp; $bar ) </p>
				<p align="left">&lt;strong&gt; This AND that &lt;/strong&gt; </p>
				<p align="left">#end </p>
				<p align="left">## logical OR </p>
				<p align="left">#if ( $foo || $bar ) </p>
				<p align="left">&lt;strong&gt;This OR That &lt;/strong&gt; </p>
				<p align="left">#end </p>
				<p align="left">##logical NOT </p>
				<p align="left">#if ( !$foo ) </p>
				<p align="left">&lt;strong&gt; NOT that &lt;/strong&gt; </p>
				<p align="left">#end </p>
				<p align="left">循环 </p>
				<p align="left">Foreach循环 </p>
				<p align="left">例子： </p>
				<p align="left">&lt;ul&gt; </p>
				<p align="left">#foreach ( $product in $allProducts ) </p>
				<p align="left">&lt;li&gt; $product &lt;/li&gt; </p>
				<p align="left">#end </p>
				<p align="left">&lt;/ul&gt; </p>
				<p align="left">每次循环$allProducts中的一个值都会赋给$product变量。 </p>
				<p align="left">$allProducts可以是一个Vector、Hashtable或者Array。分配给$product的值是一个java对象，并且可以通过变量被引用。例如：如果$product是一个java的Product类，并且这个产品的名字可以通过调用他的getName（）方法得到。 </p>
				<p align="left">现在我们假设$allProducts是一个Hashtable，如果你希望得到它的key应该像下面这样： </p>
				<p align="left">&lt;ul&gt; </p>
				<p align="left">#foreach ( $key in $allProducts.keySet() ) </p>
				<p align="left">&lt;li&gt;Key: $key -&gt; Value: $allProducts.get($key) &lt;/li&gt; </p>
				<p align="left">#end </p>
				<p align="left">&lt;/ul&gt; </p>
				<p align="left">Velocity还特别提供了得到循环次数的方法，以便你可以像下面这样作： </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">#foreach ( $customer in $customerList ) </p>
				<p align="left">&lt;tr&gt;&lt;td&gt;$velocityCount&lt;/td&gt;&lt;td&gt;$customer.Name&lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">#end </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">$velocityCount变量的名字是Velocity默认的名字，你也可以通过修改velocity.properties文件来改变它。默认情况下，计数从“1”开始，但是你可以在velocity.properties设置它是从“1”还是从“0”开始。下面就是文件中的配置： </p>
				<p align="left"># Default name of loop counter </p>
				<p align="left"># variable reference </p>
				<p align="left">directive.foreach.counter.name = velocityCount </p>
				<p align="left"># Default starting value of the loop </p>
				<p align="left"># counter variable reference </p>
				<p align="left">directive.foreach.counter.initial.value = 1 </p>
				<p align="left">include </p>
				<p align="left">#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因，被引入的本地文件只能在TEMPLATE_ROOT目录下。 </p>
				<p align="left">#inclued ( “one.txt” ) </p>
				<p align="left">如果您需要引入多个文件，可以用逗号分隔就行： </p>
				<p align="left">#include ( “one.gif”, “two.txt”, “three.htm” ) </p>
				<p align="left">在括号内可以是文件名，但是更多的时候是使用变量的： </p>
				<p align="left">#inclue ( “greetings.txt”, $seasonalstock ) </p>
				<p align="left">parse </p>
				<p align="left">#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTL并render模板。 </p>
				<p align="left">#parse( “me.vm” ) </p>
				<p align="left">就像#include，#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是，#parse只能指定单个对象。 </p>
				<p align="left">你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10。#parse是可以递归调用的，例如：如果dofoo.vm包含如下行： </p>
				<p align="left">Count down. </p>
				<p align="left">#set ( $count = 8 ) </p>
				<p align="left">#parse ( “parsefoo.vm” ) </p>
				<p align="left">All done with dofoo.vm! </p>
				<p align="left">那么在parsefoo.vm模板中，你可以包含如下VTL： </p>
				<p align="left">$count </p>
				<p align="left">#set ( $count = $count – 1 ) </p>
				<p align="left">#if ( $count &gt; 0 ) </p>
				<p align="left">#parse( “parsefoo.vm” ) </p>
				<p align="left">#else </p>
				<p align="left">All done with parsefoo.vm! </p>
				<p align="left">#end </p>
				<p align="left">的显示结果为： </p>
				<p align="left">Count down. </p>
				<p align="left">8 </p>
				<p align="left">7 </p>
				<p align="left">6 </p>
				<p align="left">5 </p>
				<p align="left">4 </p>
				<p align="left">3 </p>
				<p align="left">2 </p>
				<p align="left">1 </p>
				<p align="left">0 </p>
				<p align="left">All done with parsefoo.vm! </p>
				<p align="left">All done with dofoo.vm! </p>
				<p align="left">Stop </p>
				<p align="left">#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于debug是很有帮助的。 </p>
				<p align="left">#stop </p>
				<p align="left">Velocimacros </p>
				<p align="left">#macro script element允许模板设计者定义一段可重用的VTL template。例如： </p>
				<p align="left">#macro ( d ) </p>
				<p align="left">&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">#end </p>
				<p align="left">在上面的例子中Velocimacro被定义为d，然后你就可以在任何VTL directive中以如下方式调用它： </p>
				<p align="left">#d() </p>
				<p align="left">当你的template被调用时，Velocity将用&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;替换为#d()。 </p>
				<p align="left">每个Velocimacro可以拥有任意数量的参数――甚至0个参数，虽然定义时可以随意设置参数数量，但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro，一个参数是color另一个参数是array： </p>
				<p align="left">#macro ( tablerows $color $somelist ) </p>
				<p align="left">#foreach ( $something in $somelist ) </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=$color&gt;$something&lt;/td&lt;/tr&gt; </p>
				<p align="left">#end </p>
				<p align="left">#end </p>
				<p align="left">调用#tablerows Velocimacro： </p>
				<p align="left">#set ( $greatlakes = [ “Superior”, “Michigan”, “Huron”, “Erie”, “Ontario” ] ) </p>
				<p align="left">#set ( $color = “blue” ) </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">#tablerows( $color $greatlakes ) </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">经过以上的调用将产生如下的显示结果： </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=” blue”&gt; Superior &lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=” blue”&gt; Michigan &lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=” blue”&gt; Huron &lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=” blue”&gt; Erie &lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=” blue”&gt; Ontario &lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">Velocimacros可以在Velocity模板内实现行内定义（inline），也就意味着同一个web site内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的：它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。 </p>
				<p align="left">上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(在velocity.properties中定义)里，所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用： </p>
				<p align="left">#set ( $parts = [ “volva”, “stipe”, “annulus”, “gills”, “pileus” ] ) </p>
				<p align="left">#set ( $cellbgcol = “#CC00FF” ) </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">#tablerows( $cellbgcol $parts ) </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">上面VTL将产生如下的输出： </p>
				<p align="left">&lt;table&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; volva &lt;/td&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; stipe &lt;/td&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; annulus &lt;/td&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; gills &lt;/td&lt;/tr&gt; </p>
				<p align="left">&lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; pileus &lt;/td&lt;/tr&gt; </p>
				<p align="left">&lt;/table&gt; </p>
				<p align="left">Velocimacro arguments </p>
				<p align="left">Velocimacro可以使用以下任何元素作为参数： </p>
				<p align="left">lReference：任何以$开头的reference </p>
				<p align="left">lString literal： </p>
				<p align="left">lNumber literal： </p>
				<p align="left">lIntegerRange：[1….3]或者[$foo….$bar] </p>
				<p align="left">l对象数组：[“a”,”b”,”c”] </p>
				<p align="left">lboolean值：true、false </p>
				<p align="left">当将一个reference作为参数传递给Velocimacro时，请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro，而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如： </p>
				<p align="left">#macro ( callme $a ) </p>
				<p align="left">$a $a $a </p>
				<p align="left">#end </p>
				<p align="left">#callme( $foo.bar() ) </p>
				<p align="left">执行的结果是：reference $foo的bar（）方法被执行了三次。 </p>
				<p align="left">如果你不需要这样的特性可以通过以下方法： </p>
				<p align="left">#set ( $myval = $foo.bar() ) </p>
				<p align="left">#callme ( $myval ) </p>
				<p align="left">Velocimacro properties </p>
				<p align="left">Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide。 </p>
				<p align="left">Velocity.properties文件中的velocimacro.libraary：一个以逗号分隔的模板库列表。默认情况下，velocity查找唯一的一个库：VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。 </p>
				<p align="left">Velocity.properties文件中的velocimacro.permissions.allow.inline属性：有两个可选的值true或者false，通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros。 </p>
				<p align="left">Velocity.properties文件中的 </p>
				<p align="left">velocimacro.permissions.allow.inline.replace.global属性有两个可选值true和false，这个属性允许使用者确定inline的Velocimacro定义是否可以替代全局Velocimacro定义（比如在velocimacro.library属性中指定的文件内定义的Velocimacro）。默认情况下，此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。 </p>
				<p align="left">Velocity.properties文件中的 </p>
				<p align="left">velocimacro.permissions.allow.inline.local.scale属性也是有true和false两个可选值，默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说，如果这个属性设置为true，一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM敲门：a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected。 </p>
				<p align="left">Velocity.properties文件中的velocimacro.context.localscope属性有true和false两个可选值，默认值为false。当设置为true时，任何在Velocimacro内通过#set()对context的修改被认为是针对此velocimacro的本地设置，而不会永久的影响内容。 </p>
				<p align="left">Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时，对于一个Velocimacro的调用将自动检查原始库是否发生了变化，如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果，就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态（file.resource.loader.cache = false）。注意这个属性实际上是针对开发而非产品的。 </p>
				<p align="left">Velocimacro Trivia </p>
				<p align="left">Velocimacro必须被定义在他们被使用之前。也就是说，你的#macro()声明应该出现在使用Velocimacros之前。 </p>
				<p align="left">特别要注意的是，如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。 </p>
				<p align="left">Escaping VTL directives </p>
				<p align="left">VTL directives can be escaped with “\\”号，使用方式跟VTL的reference使用逃逸符的格式差不多。 </p>
				<p align="left">## #include( “a.txt” ) renders as &lt;ontents of a.txt&gt;(注释行) </p>
				<p align="left">#include( “a.txt” ) </p>
				<p align="left">## \\#include( “a.txt” ) renders as \\#include( “a.txt” ) </p>
				<p align="left">\\#include( “a.txt” ) </p>
				<p align="left">## \\\\#include ( “a.txt” ) renders as \\&lt;contents of a.txt&gt; </p>
				<p align="left">\\\\#include( “a.txt” ) </p>
				<p align="left">在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心（比如在一个if-else-end statement内）。下面是VTL的if-statement的典型应用： </p>
				<p align="left">#if ( $jazz ) </p>
				<p align="left">Vyacheslav Ganelin </p>
				<p align="left">#end </p>
				<p align="left">如果$jazz是ture，输出将是： </p>
				<p align="left">Vyacheslav Ganelin </p>
				<p align="left">如果$jazz是false，将没有输出。使用逃逸符将改变输出。考虑一下下面的情况： </p>
				<p align="left">\\#if ( $jazz ) </p>
				<p align="left">Vyacheslav Ganelin </p>
				<p align="left">\\#end </p>
				<p align="left">现在无论$jazz是true还是false，输出结果都是： </p>
				<p align="left">#if ( $jazz ) </p>
				<p align="left">Vyacheslav Ganelin </p>
				<p align="left">#end </p>
				<p align="left">事实上，由于你使用了逃逸符，$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的，例如： </p>
				<p align="left">\\\\#if ( $jazz ) </p>
				<p align="left">Vyacheslav Ganelin </p>
				<p align="left">\\\\#end </p>
				<p align="left">以上程序的显示结果为： </p>
				<p align="left">\\ Vyacheslav Ganelin </p>
				<p align="left">\\ </p>
				<p align="left">但是如果$jazz为false，那么将没有输出。（书上说会没有输出，但是我觉得应该还有有“\\”字符被输出。） </p>
				<p align="left">VTL：Formatting issues </p>
				<p align="left">尽管在此用户手册中VTL通常都开始一个新行，如下所示： </p>
				<p align="left">#set ( $imperial = [ “Munetaka”, “Koreyasu”, “Hisakira”, “Morikune” ] ) </p>
				<p align="left">#foreach ( $shogun in $imperial ) </p>
				<p align="left">$shogun </p>
				<p align="left">#end </p>
				<p align="left">但是像下面这种写法也是可以的： </p>
				<p align="left">Send me #set($foo = [“$10 and”,”a cake”])#foreach($a in $foo)$a #end please. </p>
				<p align="left">上面的代码可以被改写为： </p>
				<p align="left">Send me </p>
				<p align="left">#set ( $foo = [“$10 and “,”a cake”] ) </p>
				<p align="left">#foreach ( $a in $foo ) </p>
				<p align="left">$a </p>
				<p align="left">#end </p>
				<p align="left">please. </p>
				<p align="left">或者 </p>
				<p align="left">Send me </p>
				<p align="left">#set($foo = [“$10 and “,”a cake”]) </p>
				<p align="left">#foreach （$a in $foo ）$a </p>
				<p align="left">#end please. </p>
				<p align="left">这两种的输出结构将一样。 </p>
				<p align="left">其他特性和杂项 </p>
				<p align="left">math 在模板中可以使用Velocity内建的算术函数，如：加、减、乘、除 </p>
				<p align="left">#set ( $foo = $bar + 3 ) </p>
				<p align="left">#set ( $foo = $bar - 4 ) </p>
				<p align="left">#set ( $foo = $bar * 6 ) </p>
				<p align="left">#set ( $foo = $bar / 2 ) </p>
				<p align="left">当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到： </p>
				<p align="left">#set ( $foo = $bar % 5 ) </p>
				<p align="left">在Velocity内使用数学计算公式时，只能使用像-n,-2,-1,0,1,2,n这样的整数，而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。 </p>
				<p align="left">Range Operator </p>
				<p align="left">Range operator可以被用于与#set和#foreach statement联合使用。对于处理一个整型数组它是很有用的，Range operator具有以下构造形式： </p>
				<p align="left">[n..m] </p>
				<p align="left">m和n都必须是整型，而m是否大于n则无关紧要。例子： </p>
				<p align="left">First example: </p>
				<p align="left">#foreach ( $foo in [1..5] ) </p>
				<p align="left">$foo </p>
				<p align="left">#end </p>
				<p align="left">Second example: </p>
				<p align="left">#foreach ( $bar in [2..-2] ) </p>
				<p align="left">$bar </p>
				<p align="left">#end </p>
				<p align="left">Third example: </p>
				<p align="left">#set ( $arr = [0..1] ) </p>
				<p align="left">#foreach ( $i in $arr ) </p>
				<p align="left">$i </p>
				<p align="left">#end </p>
				<p align="left">Fourth example: </p>
				<p align="left">[1..3] </p>
				<p align="left">上面四个例子的输出结果为： </p>
				<p align="left">First example： </p>
				<p align="left">1 2 3 4 5 </p>
				<p align="left">Second example： </p>
				<p align="left">2 1 0 -1 -2 </p>
				<p align="left">Third example： </p>
				<p align="left">0 1 </p>
				<p align="left">Fourth example： </p>
				<p align="left">[1..3] </p>
				<p align="left">注意：range operator只在#set和#foreach中有效。 </p>
				<p align="left">Advanced Issue：Escaping and！ </p>
				<p align="left">当一个reference被“！”分隔时，并且在它之前有逃逸符时，reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下： </p>
				<p align="left">#set ( $foo = “bar” ) </p>
				<p align="left">特殊形式标准格式 </p>
				<p align="left">Render前Render后Render前Render后 </p>
				<p align="left">$\\!foo$!foo\\$foo\\$foo </p>
				<p align="left">$\\!{foo}$!{foo}\\$!foo\\$!foo </p>
				<p align="left">$\\\\!foo$\\!foo\\$!{foo}\\$!{foo} </p>
				<p align="left">$\\\\\\!foo$\\\\!foo\\\\$!{foo}\\bar </p>
				<p align="left">Velocimacro杂记 </p>
				<p align="left">Can I user a directive or another VM as an argument to a VM? </p>
				<p align="left">例如：#center ( #bold( “hello” ) ) </p>
				<p align="left">不可以。一个directive的参数使用另外一个directive是不合法的。 </p>
				<p align="left">但是，还是有些事情你可以作的。最简单的方式就是使用双引号： </p>
				<p align="left">#set ( $stuff = “#bold( ‘hello' )” ) </p>
				<p align="left">#center ( $stuff ) </p>
				<p align="left">上面的格式也可以缩写为一行： </p>
				<p align="left">#center ( “#bold( ‘hello' ) ) </p>
				<p align="left">请注意在下面的例子中参数被evaluated在Velocimacro内部，而不是在calling level。例子： </p>
				<p align="left">#macro ( inner $foo ) </p>
				<p align="left">inner : $foo </p>
				<p align="left">#end </p>
				<p align="left">#macro ( outer $foo ) </p>
				<p align="left">#set ( $bar = “outerlala” ) </p>
				<p align="left">outer : $foo </p>
				<p align="left">#end </p>
				<p align="left">#set ( $bar = ‘calltimelala' ) </p>
				<p align="left">#outer( “#inner($bar)” ) </p>
				<p align="left">输出结果为： </p>
				<p align="left">outer : inner : outerlala </p>
				<p align="left">记住Veloctiy的特性：参数的传递是By Name的。例如： </p>
				<p align="left">#macro ( foo $color ) </p>
				<p align="left">&lt;tr bgcolor = $color &gt;&lt;td&gt;Hi&lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">&lt;tr bgcolor = $color &gt;&lt;td&gt;There&lt;/td&gt;&lt;/tr&gt; </p>
				<p align="left">#end </p>
				<p align="left">#foo ( $bar.rowColor() ) </p>
				<p align="left">以上代码将导致rowColor()方法两次调用，而不是一次。为了避免这种现象的出现，我们可以按照下面的方式执行： </p>
				<p align="left">#set ( $color = $bar.rowColor() ) </p>
				<p align="left">#foo ( $color ) </p>
				<p align="left">can I register velocimacros via #parse()? </p>
				<p align="left">目前，Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。 </p>
				<p align="left">如果你试图#parse()一个包含#macro() directive的模板，这一点是需要牢记的。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。 </p>
				<p align="left">What is velocimacro autoreloading？ </p>
				<p align="left">velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false。 </p>
				<p align="left">String concatenation </p>
				<p align="left">开发人员最常问的问题是我如何作字符拼接？在java中是使用“＋”号来完成的。 </p>
				<p align="left">在VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如： </p>
				<p align="left">#set ( $size = “Big” ) </p>
				<p align="left">#set ( $name = “Ben” ) </p>
				<p align="left">The clock is $size$name. </p>
				<p align="left">输出结果将是：The clock is BigBen.。更有趣的情况是： </p>
				<p align="left">#set ( $size = “Big” ) </p>
				<p align="left">#set ( $name = “Ben” ) </p>
				<p align="left">#set ( $clokc = “$size$name” ) </p>
				<p align="left">The clock is $clock. </p>
				<p align="left">上例也会得到同样的结果。最后一个例子，当你希望混合固定字段到你的reference时，你需要使用标准格式： </p>
				<p align="left">#set ( $size = “Big” ) </p>
				<p align="left">#set ( $name = “Ben” ) </p>
				<p align="left">#set ( $clock = “${size}Tall$name” ) </p>
				<p align="left">The clock is $clock. </p>
				<p align="left">输出结果是：The clock is BigTallBen.。使用这种格式主要是为了使得$size不被解释为$sizeTall。 </p>
		</div>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/39873.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-04-07 16:57 <a href="http://www.blogjava.net/TrampEagle/articles/39873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lucene全文检索实现流程</title><link>http://www.blogjava.net/TrampEagle/articles/31642.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31642.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31642.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31642.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31642.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31642.html</trackback:ping><description><![CDATA[引自：<a href="http://www.gamvan.com/developer/java/opener/2005/12/846.html">http://www.gamvan.com/developer/java/opener/2005/12/846.html</a><br /><br />对于lucene全文检索，可以简约地看看它的7个主要类，这7个类同时也表达了处理全文检索的7个意念。因为数据库比较为人所熟悉，它又与全文检索某些理念极为相似，所以在对下面的概念说明时，将会大量在渗入数据库和全文检索间的类比。 <br />1）Document ：它的中文意思是文档，对于一个文档，通常都包括它的标题、时间、作者、内容。如果让它的意思泛化，它也有文件的意思（相信Lucene的作者在取这个类名的时候也是经过再三斟酌的），对于文件，它可以指一个TXT文本、一个HTML网页、一个PDF文件等等。总而言之，它是映射着某一个具体的文件，这就是它的单位粒度。对应于<a href="http://www.gamvan.com/database/" target="_blank"><font color="#002c99">数据库</font></a>的概念是记录，英文是Record，weblucene在索引源的xml中就原原本本地采用了这个概念，可参照&lt;weblucene_home&gt;/dump/blog.xml 。 <br /><br />2）Field ：中文意思是字段。汉语的翻译将这个单词的意思表达得更恰如其分了，意思是文字片段，它可以是一个或多个字。字段是文档（Document）的次粒度单位，也是检索的最基本单位。注意这个概念跟在搜索引擎的输入框中随意输入的那几个字或几组字不是一样的，在搜索输入框中输入的那些文字首先需要经过一个QueryParser（这也是7个类中的一个）将其分解成真正的Field。 在<a href="http://www.gamvan.com/database/" target="_blank"><font color="#002c99">数据库</font></a>中本来就有Field 这个概念，它即是Record的次级单位。在<a href="http://www.gamvan.com/database/" target="_blank"><font color="#002c99">数据库</font></a>一条或多条记录怎么被找出来的，往往就经由对Field的检索而得来。在全文检索中Document的最终取得也是差不多，它是根据现在的某个Filed来决定的。即根据文档中的某些还不完整的东西牵引出它的所需的全部。 <br />示例代码： <br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />[com.chedong.weblucene.search.WebLuceneResultSetTest] <br />Document doc = new Document(); <br />doc.add(Field.Keyword("keyword", "房地产")); <br />doc.add(Field.Keyword("keyword", "非典")); </td></tr></tbody></table><br />3）IndexWriter ：索引书写者 <br />示例代码： <br />[org.apache.lucene.index.TestIndexWriter] <br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />Document doc = new Document(); <br />doc.add(Field.UnStored("content", "aaa")); <br />try { <br />IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); <br />writer.addDocument(doc); <br />} <br />catch (IOException e) { <br />e.printStackTrace(); <br />} </td></tr></tbody></table><br />它的构造器new IndexWriter(dir, new WhitespaceAnalyzer(), true) 有三个参数，分别是Directory对象、分析器对象（这是下一个要介绍的类）和一个布尔量，是否要重写索引就是由这个布尔量标明的，如果取false则仅仅是将索引附加上来。 <br /><br />4）Analyzer ： 分析器，它将目录中的一组原始文件分析成由一组Document和它相应的次级粒度Field组成的索引。因为不同的文件类型，同样的文本其内部文件流组织形式是不一样的，所以需要不同的分析器处理不同的文件类型（如PDFAnalyzer、HtmlAnalyzer）。又因为不同的文字语言处理Field拆分（Token）上是不一样的，所以在语言角度上又有另一层分析器的类别区分，如下面这行代码是处理普通中文字符的 <br />[com.chedong.weblucene.search.WebLuceneResultSetTest] <br />IndexWriter writer = new IndexWriter(dir, new CJKAnalyzer(), true); <br /><br />5）QueryParser ：这前面在区分Field和用户输入的搜索关键字时已经提及了。（它所用的词是Query，跟<a href="http://www.gamvan.com/database/" target="_blank"><font color="#002c99">数据库</font></a>所用的是一个概念的，这个词同样的检索上让人感觉没有像Search那么费尽心思去深究。）QueryParser并没有实际地去做查询工作，它只是Parser（剖析）用户输入的东西，并把剖析的结果以一种规则的形式送给Searcher去执行一个真正的搜索。作为剖析的结果，不仅仅是Field，还有Field们相互间的关系(terms，譬如逻辑和AND，逻辑或OR， 逻辑非NOT)。 <br /><br />6）Searcher ： 搜索一个Query，将结果返回。 <br />[org.apache.lucene.search..TestNot] <br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />RAMDirectory store = new RAMDirectory(); <br />IndexWriter writer = new IndexWriter(store, new SimpleAnalyzer(), true); <br /><br />Document d1 = new Document(); <br />d1.add(Field.Text("field", "a b")); <br /><br />writer.addDocument(d1); <br />writer.optimize(); <br />writer.close(); <br /><br />Searcher searcher = new IndexSearcher(store); <br />Query query = QueryParser.parse("a NOT b", "field", new SimpleAnalyzer()); <br />//System.out.println(query); <br />Hits hits = searcher.search(query); </td></tr></tbody></table><br />对于每一个搜索器对象，它都需要指定一个索引文件路径，然后由搜索器对Query对象执行查询。 上面的这个类袖珍地对7个主要类都均有涉及。 <br /><br />7）Hits ： Lucene的类注释是：A ranked list of documents, used to hold search results. 搜索的结果不仅仅是一些文档，而且这些文档还是有级别的，这个级别是对于先后次序而言的。它对应于<a href="http://www.gamvan.com/database/" target="_blank"><font color="#002c99">数据库</font></a>中的RecordSet / ResultSet 。 <br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/31642.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:31 <a href="http://www.blogjava.net/TrampEagle/articles/31642.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lucene中文分词的高亮[highlight]显示</title><link>http://www.blogjava.net/TrampEagle/articles/31641.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Feb 2006 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/31641.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/31641.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/31641.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/31641.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/31641.html</trackback:ping><description><![CDATA[引自：<a href="http://www.gamvan.com/developer/java/opener/2005/12/849.html">http://www.gamvan.com/developer/java/opener/2005/12/849.html</a><br /><br />1、问题的来源 增加分词以后结果的准确度提高了，但是用户反映返回结果的速度很慢。原因是， Lucene 做每一篇文档的相关关键词的高亮显示时，在运行时执行了很多遍的分词操作。这样降低了性能。<br />2 、解决方法  <br />在 Lucene1.4.3 版本中的一个新功能可以解决这个问题。 Term Vector 现在支持保存 Token.getPositionIncrement() 和 Token.startOffset() 以及 Token.endOffset() 信息。利用 Lucene 中新增加的 Token 信息的保存结果以后，就不需要为了高亮显示而在运行时解析每篇文档。通过 Field 方法控制是否保存该信息。修改 HighlighterTest.java 的代码如下： <br /> <br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />// 增加文档时保存 Term 位置信息。 <br />private void addDoc(IndexWriter writer, String text) throws IOException <br />{ <br />Document d = new Document(); <br />//Field f = new Field(FIELD_NAME, text, true, true, true); <br />Field f = new Field(FIELD_NAME, text , <br />Field.Store.YES, Field.Index.TOKENIZED, <br />Field.TermVector.WITH_POSITIONS_OFFSETS); <br />d.add(f); <br />writer.addDocument(d); <br /> } <br />// 利用 Term 位置信息节省 Highlight 时间。 <br />void doStandardHighlights() throws Exception <br />{ <br />    Highlighter highlighter =new Highlighter(this,new QueryScorer(query)); <br />highlighter.setTextFragmenter(new SimpleFragmenter(20)); <br />for (int i = 0; i &lt; hits.length(); i++) <br />{ <br />String text = hits.doc(i).get(FIELD_NAME); <br />int maxNumFragmentsRequired = 2; <br />String fragmentSeparator = "..."; <br />TermPositionVector tpv = (TermPositionVector)reader.getTermFreqVector(hits.id(i),FIELD_NAME); <br />// 如果没有 stop words 去除还可以改成 TokenSources.getTokenStream(tpv,true); 进一步提速。 <br />   TokenStream tokenStream=TokenSources.getTokenStream(tpv); <br />   //analyzer.tokenStream(FIELD_NAME,new StringReader(text)); <br />   String result = <br />   highlighter.getBestFragments( <br />   tokenStream, <br />   text, <br />   maxNumFragmentsRequired, <br />       fragmentSeparator); <br />   System.out.println(" " + result); <br /> } <br />} </td></tr></tbody></table><br />最后把 highlight 包中的一个额外的判断去掉。对于中文来说没有明显的单词界限，所以下面这个判断是错误的： <br /> <br />tokenGroup.isDistinct(token) <br /> <br />这样中文分词就不会影响到查询速度了。 <br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/31641.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-20 14:30 <a href="http://www.blogjava.net/TrampEagle/articles/31641.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Prototype Meets Ruby: A Look at Enumerable, Array and Hash</title><link>http://www.blogjava.net/TrampEagle/articles/30260.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 11 Feb 2006 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30260.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30260.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30260.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30260.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30260.html</trackback:ping><description><![CDATA[原文引自:http://encytemedia.com/blog/articles/2005/12/07/prototype-meets-ruby-a-look-at-enumerable-array-and-hash<BR><BR>
<H1><A href="http://encytemedia.com/blog">Encytemedia</A></H1>
<P id=subtitle>A LOOK AT SOFTWARE DEVELOPMENT, DESIGN, AND OTHER RANDOM BITS. </P><!-- /HEADER -->
<DIV id=content><!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
       xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
       xmlns:dc="http://purl.org/dc/elements/1.1/">
<rdf:Description
    rdf:about=""
    trackback:ping="http://encytemedia.com/blog/articles/trackback/754"
    dc:title="Working With Events In Prototype"
    dc:identifier="http://encytemedia.com/blog/articles/read/754"
    dc:description="Events drive interaction for almost everything and the web is no exception. In the style of my last article on Prototype, lets take a"
    dc:creator="Justin"
    dc:date="2006-02-09T15:41:05-05:00" />
</rdf:RDF>
-->
<DIV class="post fullpost" onmouseover="if (getCookie('is_admin') == 'yes') { Element.show('admin_article'); }" onmouseout="Element.hide('admin_article');"><A class=admintools id=admin_article style="DISPLAY: none" href="http://encytemedia.com/blog/admin/content/edit/754">edit</A> 
<H2>Working With Events In Prototype</H2>
<P class=pdate>February 08, 2006</P>
<P>Events drive interaction for almost everything and the web is no exception. In the style of my <A href="http://encytemedia.com/blog/articles/2005/12/07/prototype-meets-ruby-a-look-at-enumerable-array-and-hash">last article on Prototype</A>, lets take a code-heavy look at how Prototype lets us work with events.</P>
<P><STRONG><EM>NOTE: This article uses Prototype 1.5.0_pre0</STRONG></EM></P>
<H3>Basic event handling</H3>
<P>The syntax for working with events looks like the code below.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>,</SPAN> <SPAN class=ident>name</SPAN><SPAN class=punct>,</SPAN> <SPAN class=ident>observer</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>[</SPAN><SPAN class=ident>useCapture</SPAN><SPAN class=punct>]);</SPAN></CODE></PRE></DIV>
<P>Assuming for a moment that we want to observe when a link was clicked, we could do the following:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=punct>&lt;</SPAN><SPAN class=ident>a</SPAN> <SPAN class=ident>id</SPAN><SPAN class=punct>="</SPAN><SPAN class=string>clicker</SPAN><SPAN class=punct>"</SPAN> <SPAN class=ident>href</SPAN><SPAN class=punct>="</SPAN><SPAN class=string>http://foo.com</SPAN><SPAN class=punct>"&gt;</SPAN><SPAN class=constant>Click</SPAN> <SPAN class=ident>me</SPAN><SPAN class=punct>&lt;/</SPAN><SPAN class=regex>a&gt;
Event.observe('clicker', 'click', function(event){ alert('clicked!');});</SPAN></CODE></PRE></DIV>
<P>If we wanted to get the element that fired the event, we'd do this:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>clicker</SPAN><SPAN class=punct>',</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>){</SPAN> <SPAN class=ident>alert</SPAN><SPAN class=punct>(</SPAN><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>));});</SPAN></CODE></PRE></DIV>
<H4>Observing keystrokes</H4>
<P>If we wanted to observe keystrokes for the entire document, we could do the following:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>document</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>keypress</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>){</SPAN> <SPAN class=keyword>if</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>keyCode</SPAN> <SPAN class=punct>==</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>KEY_TAB</SPAN><SPAN class=punct>)</SPAN> <SPAN class=ident>alert</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>Tab Pressed</SPAN><SPAN class=punct>');});</SPAN></CODE></PRE></DIV>
<P>And lets say we wanted to keep track of what has been typed into your snazzy live-search box:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>search</SPAN><SPAN class=punct>',</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>keypress</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN> <SPAN class=constant>Element</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>update</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>search-results</SPAN><SPAN class=punct>',</SPAN> <SPAN class=global>$F</SPAN><SPAN class=punct>(</SPAN><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)));});</SPAN></CODE></PRE></DIV>
<P>Prototype defines properties inside the event object for some of the more common keys, so feel free to dig around in Prototype to see which ones those are.</P>
<P>A final note on keypress events; If you'd like to detect a left click you can use <CODE>Event.isLeftClick(event)</CODE>.</P>
<H4>Getting the coordinates of the mouse pointer</H4>
<P>Drag and drop, dynamic element resizing, games, and much more all require the ability to track the X and Y location of the mouse. Prototype makes this fairly simple. The code below tracks the X and Y position of the mouse and spits out those values into an input box named <EM>mouse</EM>.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby ">   <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>document</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>mousemove</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>){</SPAN><SPAN class=global>$(</SPAN><SPAN class=punct>'</SPAN><SPAN class=string>mouse</SPAN><SPAN class=punct>').</SPAN><SPAN class=ident>value</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>X: </SPAN><SPAN class=punct>"</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>pointerX</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>px Y: </SPAN><SPAN class=punct>"</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>pointerY</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>px</SPAN><SPAN class=punct>";});</SPAN></CODE></PRE></DIV>
<P>If we wanted to observe the mouse location when it was hovering over a certain element, we'd just change the <EM>document</EM> argument to the id or element that was relevant.</P>
<H4>Stopping Propagation</H4>
<P><CODE>Event.stop(event)</CODE> will stop the propagation of an event but there is a bug in Safari 2.0.3 that causes this to behave unexpectedly. Ryan from <A href="http://particletree.com/">Particletree</A> has posted an in-depth <A href="http://particletree.com/notebook/eventstop/">article on <CODE>Event.stop</CODE></A> with a workaround for Safari.</P>
<P>The good news is that this bug is fixed in the <A href="http://nightly.webkit.org/builds/">Webkit version of Safari</A>. You can check out <A href="http://bugzilla.opendarwin.org/attachment.cgi?id=4088">this test</A> in Safari 2.0.3 and Webkit to see the results of both.</P>
<H3>Events, Binding, and Objects</H3>
<P>Everything has been fairly straight forward so far, but things start getting a little tricker when you need to work with events in and object-oriented environment. You have to deal with binding and funky looking syntax that might take a moment to get your head around.</P>
<P>Lets look at some code so you can get a better understanding of what I'm talking about.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>EventDispenser</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=constant>Class</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>create</SPAN><SPAN class=punct>();</SPAN>
<SPAN class=constant>EventDispenser</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>prototype</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=punct>{</SPAN>
  <SPAN class=ident>initialize</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>list</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=ident>list</SPAN><SPAN class=punct>;</SPAN>

    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Observe</SPAN> <SPAN class=ident>clicks</SPAN> <SPAN class=ident>on</SPAN> <SPAN class=ident>our</SPAN> <SPAN class=ident>list</SPAN> <SPAN class=ident>items</SPAN>     
    <SPAN class=global>$$</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string> li</SPAN><SPAN class=punct>").</SPAN><SPAN class=ident>each</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
      <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagName</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>
    <SPAN class=punct>}.</SPAN><SPAN class=ident>bind</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>

    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Observe</SPAN> <SPAN class=keyword>when</SPAN> <SPAN class=ident>a</SPAN> <SPAN class=ident>key</SPAN> <SPAN class=ident>on</SPAN> <SPAN class=ident>the</SPAN> <SPAN class=ident>keyboard</SPAN> <SPAN class=ident>is</SPAN> <SPAN class=ident>pressed</SPAN><SPAN class=punct>.</SPAN> <SPAN class=ident>In</SPAN> <SPAN class=ident>the</SPAN> <SPAN class=ident>observer</SPAN><SPAN class=punct>,</SPAN> <SPAN class=ident>we</SPAN> <SPAN class=ident>check</SPAN> <SPAN class=keyword>for</SPAN> 
    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=ident>the</SPAN> <SPAN class=ident>tab</SPAN> <SPAN class=ident>key</SPAN> <SPAN class=keyword>and</SPAN> <SPAN class=ident>alert</SPAN> <SPAN class=ident>a</SPAN> <SPAN class=ident>message</SPAN> <SPAN class=keyword>if</SPAN> <SPAN class=ident>it</SPAN> <SPAN class=ident>is</SPAN> <SPAN class=ident>pressed</SPAN><SPAN class=punct>.</SPAN>
    <SPAN class=ident>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>document</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>keypress</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>onKeyPress</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>

    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Observe</SPAN> <SPAN class=ident>our</SPAN> <SPAN class=ident>fake</SPAN> <SPAN class=ident>live</SPAN> <SPAN class=ident>search</SPAN> <SPAN class=ident>box</SPAN><SPAN class=punct>.</SPAN>  <SPAN class=ident>When</SPAN> <SPAN class=ident>a</SPAN> <SPAN class=ident>user</SPAN> <SPAN class=ident>types</SPAN> <SPAN class=ident>something</SPAN> <SPAN class=ident>into</SPAN> <SPAN class=ident>the</SPAN> <SPAN class=ident>box</SPAN><SPAN class=punct>,</SPAN> 
    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=ident>the</SPAN> <SPAN class=ident>observer</SPAN> <SPAN class=ident>will</SPAN> <SPAN class=ident>take</SPAN> <SPAN class=ident>that</SPAN> <SPAN class=ident>value</SPAN><SPAN class=punct>(-</SPAN><SPAN class=number>1</SPAN><SPAN class=punct>)</SPAN> <SPAN class=keyword>and</SPAN> <SPAN class=ident>update</SPAN> <SPAN class=ident>our</SPAN> <SPAN class=ident>search</SPAN><SPAN class=punct>-</SPAN><SPAN class=ident>results</SPAN> <SPAN class=ident>div</SPAN> <SPAN class=ident>with</SPAN> <SPAN class=ident>it</SPAN><SPAN class=punct>.</SPAN>
    <SPAN class=ident>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>search</SPAN><SPAN class=punct>',</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>keypress</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>onSearch</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>

    <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>document</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>mousemove</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>onMouseMove</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>
  <SPAN class=punct>},</SPAN>


  <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Arbitrary</SPAN> <SPAN class=ident>functions</SPAN> <SPAN class=ident>to</SPAN> <SPAN class=ident>respond</SPAN> <SPAN class=ident>to</SPAN> <SPAN class=ident>events</SPAN>
  <SPAN class=ident>showTagName</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=ident>alert</SPAN><SPAN class=punct>(</SPAN><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>).</SPAN><SPAN class=ident>tagName</SPAN><SPAN class=punct>);</SPAN>
  <SPAN class=punct>},</SPAN>


  <SPAN class=ident>onKeyPress</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=ident>var</SPAN> <SPAN class=ident>code</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=ident>event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>keyCode</SPAN><SPAN class=punct>;</SPAN>
    <SPAN class=keyword>if</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>code</SPAN> <SPAN class=punct>==</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>KEY_TAB</SPAN><SPAN class=punct>)</SPAN> <SPAN class=ident>alert</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>Tab key was pressed</SPAN><SPAN class=punct>');</SPAN>
  <SPAN class=punct>},</SPAN>

  <SPAN class=ident>onSearch</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=constant>Element</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>update</SPAN><SPAN class=punct>('</SPAN><SPAN class=string>search-results</SPAN><SPAN class=punct>',</SPAN> <SPAN class=global>$F</SPAN><SPAN class=punct>(</SPAN><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)));</SPAN>
  <SPAN class=punct>},</SPAN>

  <SPAN class=ident>onMouseMove</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=global>$(</SPAN><SPAN class=punct>'</SPAN><SPAN class=string>mouse</SPAN><SPAN class=punct>').</SPAN><SPAN class=ident>value</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>X: </SPAN><SPAN class=punct>"</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>pointerX</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>px Y: </SPAN><SPAN class=punct>"</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>pointerY</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string>px</SPAN><SPAN class=punct>";</SPAN>
  <SPAN class=punct>}</SPAN>

<SPAN class=punct>}</SPAN></CODE></PRE></DIV>
<P>Whoa! What's going on here? Well, we've defined our a custom class <CODE>EventDispenser</CODE>. We're going to be using this class to setup events for our document. Most of this code is a rewrite of the code we looked at earlier except this time, we are working from inside an object.</P>
<P><A href="http://encytemedia.com/demo/prototype/events/">View a live example of our document »</A></P>
<P>Looking at the <CODE>initialize</CODE> method, we can really see how things are different now. Take a look at the code below:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby ">    <SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Observe</SPAN> <SPAN class=ident>clicks</SPAN> <SPAN class=ident>on</SPAN> <SPAN class=ident>our</SPAN> <SPAN class=ident>list</SPAN> <SPAN class=ident>items</SPAN>     
    <SPAN class=global>$$</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string> li</SPAN><SPAN class=punct>").</SPAN><SPAN class=ident>each</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
      <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagName</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN>
    <SPAN class=punct>}.</SPAN><SPAN class=ident>bind</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN></CODE></PRE></DIV>
<P>We've got iterators, binding and all sorts of stuff going on. Lets break down what this chunk of code is doing.</P>
<P>First we are hunting for a collection of elements based on it's css selector. This uses the new Prototype selector function <CODE>$$</CODE>. After we've found the list items we are dealing with we send those into an <CODE>each</CODE> iteration where we will add our observers.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagName</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN></CODE></PRE></DIV>
<P>Now looking at the code above, you'll notice the <CODE>bindAsEventListener</CODE> function. This takes the method before it <CODE>showTagName</CODE> and treats it as the method that will be triggered when, in this case, someone clicks one of our list items.</P>
<P>You'll also notice we pass <CODE>this</CODE> as an argument to the <CODE>bindAsEventListener</CODE> function. This simply allows us to reference the object in context <CODE>EventDispenser</CODE> inside our function <CODE>showTagName</CODE>.</P>
<P><STRONG><EM>NOTE: If you prefer the jargon packed explanation of bindAsEventListener, see the <A href="http://wiki.script.aculo.us/scriptaculous/show/Function.prototype.bindAsEventListener">Script.aculo.us wiki</A>.</STRONG></EM></P>
<P>Moving on, you'll see <CODE>bind(this)</CODE> attached to our iterator function. This really has nothing to do with events, it is only here to allow me to use <CODE>this</CODE> inside the iterator. If we didn't use <CODE>bind(this)</CODE>, I couldn't reference the method <CODE>showTagName</CODE> inside the iterator.</P>
<P>Ok, so we'll move on to looking at our methods that actually get called when an event occurs. Since we've been dealing with <CODE>showTagName</CODE>, lets look at it.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby ">  <SPAN class=ident>showTagName</SPAN><SPAN class=punct>:</SPAN> <SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
    <SPAN class=ident>alert</SPAN><SPAN class=punct>(</SPAN><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>element</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>event</SPAN><SPAN class=punct>).</SPAN><SPAN class=ident>tagName</SPAN><SPAN class=punct>);</SPAN>
  <SPAN class=punct>}</SPAN></CODE></PRE></DIV>
<P>As you can see, this function accepts one argument--the event. In order for us to get the element which fired the event we need to pass that argument to <CODE>Event.element</CODE>. Now we can manipulate it at will.</P>
<P>This covers the most confusing parts of our code. The text above is also relevant to the remaining parts of our code. If there is anything about this you don't understand, feel free to say so.</P>
<H4>Removing Event Listeners</H4>
<P>This one threw me for a loop the first time I tried to use it. I tried something similar to what I did in the <CODE>Event.observe</CODE> call with the exception of using <CODE>stopObserving</CODE>, but nothing seemed to change. In other words, <STRONG>the code below does NOT work</STRONG>.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=global>$$</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string> li</SPAN><SPAN class=punct>").</SPAN><SPAN class=ident>each</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
  <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>stopObserving</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagName</SPAN><SPAN class=punct>);</SPAN>
<SPAN class=punct>}.</SPAN><SPAN class=ident>bind</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN></CODE></PRE></DIV>
<P>What's the deal here? The reason this doesn't work is because there is no pointer to the observer. This means that when we passed <CODE>this.showTagName</CODE> in the <CODE>Event.observe</CODE> method before hand, we passed it as an anonymous function. We can't reference an anonymous function because it simply doesn't have a pointer.</P>
<P>So how do we get the job done? All we need to do is give the observing function a pointer, or the jargon free version: Set a variable that points to <CODE>this.showTagName</CODE>. Ok, lets change our code a bit.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagObserver</SPAN> <SPAN class=punct>=</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagName</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>bindAsEventListener</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>);</SPAN>

<SPAN class=punct>/</SPAN><SPAN class=regex></SPAN><SPAN class=punct>/</SPAN> <SPAN class=constant>Observe</SPAN> <SPAN class=ident>clicks</SPAN> <SPAN class=ident>on</SPAN> <SPAN class=ident>our</SPAN> <SPAN class=ident>list</SPAN> <SPAN class=ident>items</SPAN>     
<SPAN class=global>$$</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string> li</SPAN><SPAN class=punct>").</SPAN><SPAN class=ident>each</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
  <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>observe</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagObserver</SPAN><SPAN class=punct>);</SPAN>
<SPAN class=punct>}.</SPAN><SPAN class=ident>bind</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN></CODE></PRE></DIV>
<P>Now we can remove the event listeners from our list like this:</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=global>$$</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>list</SPAN> <SPAN class=punct>+</SPAN> <SPAN class=punct>"</SPAN><SPAN class=string> li</SPAN><SPAN class=punct>").</SPAN><SPAN class=ident>each</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>function</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>)</SPAN> <SPAN class=punct>{</SPAN>
  <SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>stopObserving</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>item</SPAN><SPAN class=punct>,</SPAN> <SPAN class=punct>'</SPAN><SPAN class=string>click</SPAN><SPAN class=punct>',</SPAN> <SPAN class=ident>this</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>showTagObserver</SPAN><SPAN class=punct>);</SPAN>
<SPAN class=punct>}.</SPAN><SPAN class=ident>bind</SPAN><SPAN class=punct>(</SPAN><SPAN class=ident>this</SPAN><SPAN class=punct>));</SPAN></CODE></PRE></DIV>
<P>One final note on removing event listeners. If you have an instance where you want to simply remove all observes in one big swoop, you can use <CODE>unloadCache</CODE>.</P>
<DIV class=typocode><PRE><CODE class="typocode_ruby "><SPAN class=constant>Event</SPAN><SPAN class=punct>.</SPAN><SPAN class=ident>unloadCache</SPAN><SPAN class=punct>();</SPAN></CODE></PRE></DIV>
<H3>Summing Up</H3>
<P>That pretty much sums up events in Prototype. If you find any errors, please let me know. The great thing about writing articles such as this is that I also learn so much in the process and develop a deeper understanding of what I'm writing about. For instance, I found out the removing event listeners bit <EM>while</EM> I was writing this article. If you've got anything you'd like to contribute, feel free to chime in. Until next time, Happy Prototyping!</P>
<H3>Related Reading</H3>
<UL>
<LI><A href="http://encytemedia.com/blog/articles/2005/12/07/prototype-meets-ruby-a-look-at-enumerable-array-and-hash">Prototype Meets Ruby: A Look at Enumerable, Array and Hash</A> 
<LI><A href="http://www.listible.com/list/prototype-js-documentation">Listible Prototype Resources</A> 
<LI><A href="http://wiki.script.aculo.us/scriptaculous/show/Prototype">Script.aculo.us Wiki</A> 
<LI><A href="http://www.sergiopereira.com/articles/prototype.js.html">Using Prototype.js 1.4</A> 
<LI><A href="http://particletree.com/notebook/prototype-and-the-this-keyword/">Prototype and the this keyword</A> 
<LI><A href="http://particletree.com/features/quick-guide-to-prototype/">Quick Guide To Prototype</A> 
<LI><A href="http://particletree.com/notebook/eventstop/">Event.stop</A> </LI></UL></DIV></DIV><img src ="http://www.blogjava.net/TrampEagle/aggbug/30260.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-11 14:04 <a href="http://www.blogjava.net/TrampEagle/articles/30260.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Maven 让事情变得简单</title><link>http://www.blogjava.net/TrampEagle/articles/30036.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 09 Feb 2006 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30036.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30036.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30036.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30036.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30036.html</trackback:ping><description><![CDATA[引自：<a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/">http://www-128.ibm.com/developerworks/cn/java/j-maven/</a><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="100%"><h1><span style="COLOR: #999999">项目管理: </span>Maven 让事情变得简单</h1><p id="subtitle">给您的下一个 Java 构建添加项目管理特性</p><img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" /></td><td class="no-print" width="192"><img height="18" alt="developerWorks" src="http://www-128.ibm.com/developerworks/i/dw.gif" width="192" /></td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td width="100%"><table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td><table cellspacing="0" cellpadding="0" width="150" border="0"><tbody><tr><td class="v14-header-1-small">文档选项</td></tr></tbody></table><table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="no-padding" width="150"><table cellspacing="0" cellpadding="0" width="143" border="0"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /><form name="email" action="https://www-130.ibm.com/developerworks/secure/email-it.jsp"><input type="hidden" value="尽管 Ant 对于构建 Java 程序而言是事实上的标准工具，但这个工具在许多方面都不胜任项目管理任务。相反，Ant 提供的东西，Maven（出自 Apache Jakarta 项目的高级项目管理工具）都能提供，而且更多。Java 开发人员 Charles Chan 将介绍 Maven 的特性，并循序渐进地指导您进行一次完整的 Maven 项目设置。" name="body" /><input type="hidden" value="项目管理: Maven 让事情变得简单" name="subject" /><input type="hidden" value="cn" name="lang" /><script language="JavaScript" type="text/javascript"><!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//--></script><tbody><tr valign="top"><td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td><td width="16"><img height="16" alt="将此页作为电子邮件发送" src="http://www.ibm.com/i/v14/icons/em.gif" width="16" vspace="3" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b><font color="#5c81a7" size="2">将此页作为电子邮件发送</font></b></a></p></td></tr><noscript><tr valign="top"><td width="8"><img height="1" alt="" src="//www.ibm.com/i/c.gif" width="8" /></td><td width="16"><img height="16" alt="" src="//www.ibm.com/i/c.gif" width="16" /></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></noscript></tbody></form></table></td></tr></tbody></table></td></tr></tbody></table><br /><table cellspacing="0" cellpadding="0" width="150" border="0"><tbody><tr><td class="v14-header-1-small">对此页的评价</td></tr></tbody></table><table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="no-padding" width="150"><table cellspacing="0" cellpadding="0" width="143" border="0"><tbody><tr valign="top"><td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td><td><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width="16" vspace="3" border="0" /></td><td width="125"><p><a class="smallplainlink" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#rate"><b><font color="#996699" size="2">帮助我们改进这些内容</font></b></a></p></td></tr></tbody></table></td></tr></tbody></table><br /></td></tr></tbody></table><p>级别: 初级</p><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#author"><font color="#996699">Charles Chan</font></a>, 高级软件开发人员, Finetix LLC<br /></p><p>2003 年 7 月 30 日</p><blockquote>尽管 Ant 对于构建 Java 程序而言是事实上的标准工具，但这个工具在许多方面都不胜任项目管理任务。相反，Ant 提供的东西，Maven（出自 Apache Jakarta 项目的高级项目管理工具）都能提供，而且更多。Java 开发人员 Charles Chan 将介绍 Maven 的特性，并循序渐进地指导您进行一次完整的 Maven 项目设置。</blockquote><p>目前，绝大多数开发人员都把 Ant 当作 Java 编程项目的标准构建工具。遗憾的是，Ant 的项目管理工具（作为 <code>make</code> 的替代工具）不能满足绝大多数开发人员的需要。通过检查 Ant 构建文件，很难发现项目的相关性信息和其它元信息（如开发人员／拥有者、版本或站点主页）。 </p><p>Maven 除了以程序构建能力为特色之外，还提供 Ant 所缺少的高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性，所以常常用两三行 Maven 构建脚本就可以构建简单的项目，而使用 Ant 则需要十几行。事实上，由于 Maven 的面向项目的方法，许多 Apache Jakarta 项目现在使用 Maven，而且公司项目采用 Maven 的比例在持续增长。</p><p><a name="1"><span class="atitle"><font face="Arial" size="4">Maven vs Ant</font></span></a></p><p>那么，Maven 和 Ant 有什么不同呢？在回答这个问题以前，我要强调一点：Maven 和 Ant 针对构建问题的两个不同方面。Ant 为 Java 技术开发项目提供跨平台构建任务。Maven 本身描述项目的高级方面，它从 Ant 借用了绝大多数构建任务。因此，由于 Maven 和 Ant 代表两个差异很大的工具，所以我将只说明这两个工具的等同组件之间的区别，如表 1 所示。</p><p><a name=" "><span class="smalltitle"><strong><font face="Arial">表 1. Maven vs Ant</font></strong></span></a></p><p></p><table cellspacing="0" cellpadding="3" width="600" border="1"><tbody><tr><td><strong><font face="Arial"></font></strong></td><td><strong>Maven</strong></td><td><b>Ant</b></td></tr><tr><td>标准构建文件</td><td>project.xml 和 maven.xml</td><td>build.xml</td></tr><tr><td>特性处理顺序</td><td><ol><li>${maven.home}/bin/driver.properties 
</li><li>${project.home}/project.properties 
</li><li>${project.home}/build.properties 
</li><li>${user.home}/build.properties 
</li><li>通过 -D 命令行选项定义的系统特性 </li></ol><b>最后一个</b>定义起决定作用。 </td><td><ol><li>通过 -D 命令行选项定义的系统特性 
</li><li>由 <code>&lt;property&gt;</code> 任务装入的特性 </li></ol><b>第一个</b>定义最先被处理。 </td></tr><tr><td>构建规则</td><td>构建规则更为动态（类似于编程语言）；它们是基于 Jelly 的可执行 XML。</td><td>构建规则或多或少是静态的，除非使用 <code>&lt;script&gt;</code> 任务。（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>以获得相关教程。） </td></tr><tr><td>扩展语言</td><td>插件是用 Jelly（XML）编写的。</td><td>插件是用 Java 语言编写的。</td></tr><tr><td>构建规则可扩展性</td><td>通过定义 <code>&lt;preGoal&gt;</code> 和 <code>&lt;postGoal&gt;</code> 使构建 goal 可扩展。 </td><td>构建规则不易扩展；可通过使用 <code>&lt;script&gt;</code> 任务模拟 <code>&lt;preGoal&gt;</code> 和 <code>&lt;postGoal&gt;</code> 所起的作用。 </td></tr></tbody></table><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="2"><span class="atitle"><font face="Arial" size="4">Maven 的主要组件</font></span></a></p><p>既然您了解了 Maven 和 Ant 之间的区别，让我们来研究 Maven 的主要组件，如图 1 所示。</p><br /><a name="N100FE"><b>图 1. Maven 的主要组件 </b></a><br /><img height="183" alt="Maven 的主要组件" src="http://www-128.ibm.com/developerworks/cn/java/j-maven/images/overview.gif" width="458" /><br /><p><a name="N1010B"><span class="smalltitle"><strong><font face="Arial">项目对象模型</font></strong></span></a></p><p>项目对象模型（Project Object Model，POM）描述项目的各个方面。尽管对于 POM 的物理表示没有内在的限制，但 Maven 开发人员通常使用一个 XML 项目文件（project.xml）。该 XML 文件格式由位于 Maven 安装目录中的 XML 模式（maven-project.xsd）定义。</p><p>通常，project.xml 文件由三个主要部分组成：</p><ul><li>项目管理部分包括项目的组织、开发人员名单、源代码位置和错误跟踪系统 URL 等信息。 
</li><li>项目相关性部分包括关于项目相关性的信息。当前 Maven 实现（1.0 beta 测试版 8）仅支持 JAR 文件相关性。 
</li><li>项目构建和报告部分包含项目构建信息（如源代码目录、单元测试用例目录）和要在构建中生成的报告。 </li></ul><p>清单 1 显示了带注释的样本 project.xml 文件。因为 project.xml 文件中的许多元素都是可选的，所以，随着您对 Maven 理解的加深，可以逐步使用不同的 Maven 特性。 <i>注：在以下代码中，可选的元素都以“可选的（OPTIONAL）”标明。</i></p><p>主文档包含项目的唯一标识和组标识。事实证明，当项目包括多个子项目时，组标识非常有用。所有的子项目应共享同一组标识，但每个子项目应有不同的 <code>&lt;id&gt;</code> 。 </p><br /><a name="N10130"><b>清单 1. 主 project.xml 框架</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;

&lt;!-- A project file's root element --&gt;
&lt;project&gt;
  &lt;!-- The POM version. This tag is currently unused. --&gt;
  &lt;pomVersion&gt;3&lt;/pomVersion&gt;

  &lt;!-- A project group id. If present, the id serves as the project's
        directory name in the repository --&gt;
  &lt;groupId&gt;crayola-group&lt;/groupId&gt;

  &lt;!-- A unique project identifier. The project identifier and its
        version number often generate file/directory names during the
        build. For example, a project JAR file follows the
        &lt;id&gt;-&lt;version&gt; naming convention. --&gt;
  &lt;id&gt;crayola&lt;/id&gt;

  &lt;!-- A short name for the project --&gt;
  &lt;name&gt;Crayola Professional&lt;/name&gt;

  &lt;!-- The project version number. Maven does not enforce a particular
        version numbering scheme. --&gt;
  &lt;currentVersion&gt;0.0.1&lt;/currentVersion&gt;

  ...
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project management section                                --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  ...
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project dependency section                                --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  ...
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project build and reports section                         --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  ...
&lt;/project&gt;
</font></code></pre></td></tr></tbody></table><br /><p>项目管理部分（如清单 2 所示）主要包括可选项。在此部分中指定开发人员名单（带有正确的标识），当您希望获得更改日志（Change Log）报告和开发活动（Development Activity）报告时尤其要这么做。</p><br /><a name="N1013D"><b>清单 2. 项目管理部分</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">  
...
&lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project management section                                --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;

  &lt;!-- Details of the organization holding the project. Only the name
        is required. --&gt;
  &lt;organization&gt;
    &lt;name&gt;Markers Inc.&lt;/name&gt;
    &lt;url&gt;http://w3.markers.com/&lt;/url&gt;
    
&lt;logo&gt;http://w3.markers.com/logo/company-logo.gif&lt;/logo&gt;
  &lt;/organization&gt;

  &lt;!-- (OPTIONAL) Year of inception --&gt;
  &lt;inceptionYear&gt;2003&lt;/inceptionYear&gt;

  &lt;!-- (OPTIONAL) Project main package --&gt;
  &lt;package&gt;com.markers.crayola.*&lt;/package&gt;

  &lt;!-- (OPTIONAL) Project logo picture (URL) --&gt;
  &lt;logo&gt;http://w3.markers.com/logo/crayola.gif&lt;/logo&gt;

  &lt;!-- (OPTIONAL) GUMP repository ID. Useful only if you use GUMP. --&gt;
  &lt;gumpRepositoryId&gt;crayola&lt;/gumpRepositoryId&gt;

  &lt;!-- (OPTIONAL) Project description --&gt;
  &lt;description&gt;...&lt;/description&gt;

  &lt;!-- (OPTIONAL) Short project description --&gt;
  &lt;shortDescription&gt;...&lt;/shortDescription&gt;

  &lt;!-- (OPTIONAL) Project site URL --&gt;
  &lt;url&gt;http://w3.markers.com/crayola&lt;/url&gt;

  &lt;!-- (OPTIONAL) Issue-tracking system URL --&gt;
  
&lt;issueTrackingUrl&gt;http://w3.markers.com/jira/crayola&lt;/issueTrackingUrl&gt;

  &lt;!-- (OPTIONAL) Project site address. --&gt;
  &lt;siteAddress&gt;w3.markers.com&lt;/siteAddress&gt;

  &lt;!-- (OPTIONAL) Project-site deploy directory (physical location) --&gt;
  &lt;siteDirectory&gt;/www/crayola/site/&lt;/siteDirectory&gt;

  &lt;!-- (OPTIONAL) Project distribution directory (physical location) --&gt;
  
&lt;distributionDirectory&gt;/www/crayola/builds/&lt;/distributionDirectory&gt;

  &lt;!-- (OPTIONAL) Project source-repository information --&gt;
  &lt;repository&gt;
    
&lt;connection&gt;
scm:cvs:pserver:anoncvs@cvs.markers.com:/home/cvspublic:crayola
&lt;/connection&gt;
    &lt;url&gt;http://cvs.markers.com/viewcvs/crayola/&lt;/url&gt;
  &lt;/repository&gt;

  &lt;!-- (OPTIONAL) Mailing list information --&gt;
  &lt;mailingLists&gt;
    &lt;mailingList&gt;
      &lt;name&gt;Dev List&lt;/name&gt;
      
&lt;subscribe&gt;dev-subscribe@crayola.markers.com&lt;/subscribe&gt;
      
&lt;unsubscribe&gt;dev-unsubscribe@crayola.markers.com&lt;/unsubscribe&gt;
    &lt;/mailingList&gt;
    ...
  &lt;/mailingLists&gt;

  &lt;!-- Developers involved in this project --&gt;
  &lt;developers&gt;
    &lt;developer&gt;
      &lt;name&gt;John Smith&lt;/name&gt;
      &lt;id&gt;jsmith&lt;/id&gt;
      &lt;email&gt;jsmith@markers.com&lt;/email&gt;
    &lt;/developer&gt;
    ...
  &lt;/developers&gt;
</font></code></pre></td></tr></tbody></table><br /><p>将清单 3 中的信息与一个中央构件资源库一起使用，将消除几个常见的构建问题（包括错误配置的 <code>CLASSPATH</code> 或相关性-版本不匹配）。 </p><br /><a name="N1014E"><b>清单 3. 项目相关性部分</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">  &lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project dependency section                                --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;

  &lt;dependencies&gt;

    &lt;!-- This project depends on the JAR file "commons-beanutils-1.5.jar"
          in the Maven repository's commons-beanutils/jars subdirectory
          (more about repository later). --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-beanutils&lt;/groupId&gt;
      &lt;artifactId&gt;commons-beanutils&lt;/artifactId&gt;
      &lt;version&gt;1.5&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;!-- This project depends on the JAR file "commons-lib-2.1.jar" in
          the Maven repository's markers/jars subdirectory. --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;markers&lt;/groupId&gt;
      &lt;artifactId&gt;commons-lib&lt;/artifactId&gt;
      &lt;version&gt;2.1&lt;/version&gt;
    &lt;/dependency&gt;

  &lt;/dependencies&gt;
</font></code></pre></td></tr></tbody></table><br /><p>项目构建和报告部分（如清单 4 所示）包含用于配置某些 Maven 插件的重要构建和报告信息。例如，可以配置 Maven 在站点文档生成时包含还是排除某些报告。</p><br /><a name="N1015B"><b>清单 4. 项目构建部分</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">  ...
  &lt;!-- 
---------------------------------------------------------------- --&gt;
  &lt;!-- Project build and reports section                         --&gt;
  &lt;!-- 
---------------------------------------------------------------- --&gt;

  &lt;build&gt;

    &lt;!-- (OPTIONAL) Build notification email address. --&gt;
    &lt;nagEmailAddress&gt;jsmith@markers.com&lt;/nagEmailAddress&gt;

    &lt;!-- (OPTIONAL) Defines where the Java source resides. --&gt;
    &lt;sourceDirectory&gt;src/java&lt;/sourceDirectory&gt;

    &lt;!-- (OPTIONAL) Defines where the Java source for unit test-cases
          resides. --&gt;
    
&lt;unitTestSourceDirectory&gt;test/java&lt;/unitTestSourceDirectory&gt;

    &lt;!-- (OPTIONAL) Unit test-case file pattern. --&gt;
    &lt;unitTest&gt;
      &lt;includes&gt;
        &lt;include&gt;**/*Test.java&lt;/include&gt;
      &lt;/includes&gt;
    &lt;/unitTest&gt;

    &lt;!-- (OPTIONAL) Resources packaged inside the JAR file. --&gt;
    &lt;resources/&gt;

    &lt;!-- (OPTIONAL) The reports tag lets you select which reports you
          want generated for your site. In this case, only the checkstyle
          report will generate. --&gt;
  &lt;/build&gt;

  &lt;reports&gt;
    &lt;report&gt;
      maven-checkstyle-plugin
    &lt;/report&gt;
  &lt;/reports&gt;
</font></code></pre></td></tr></tbody></table><br /><p>项目依靠库来实现其功能。例如，您的项目可能依靠 log4j 进行日志记录，依靠 Xalan 进行 XSLT 转换。对于 J2EE 项目，Web 组件可能依靠 EJB 组件来执行业务操作。Maven 可以让您用它的 POM 来表示不同的相关性。您可以用表 2 所示的标记在 project.xml 文件中描述每一个相关性。</p><p><a name=" "><span class="smalltitle"><strong><font face="Arial">表 2. 项目相关性部分</font></strong></span></a></p><p></p><table cellspacing="0" cellpadding="3" width="100%" border="1"><tbody><tr><td><strong>groupId</strong></td><td>告诉 Maven 资源库内哪个子目录中包含相关性文件。</td></tr><tr><td><b>artifactId</b></td><td>告诉 Maven 该构件的唯一标识。</td></tr><tr><td><b>version</b></td><td>表示相关性的版本号。</td></tr><tr><td><b>jar</b></td><td>（可选的）表示相关性的 JAR 文件。在绝大多数情况下，可以从相关性的 <code>&lt;artifactId&gt;</code> 和 <code>&lt;version&gt;</code> 构造 JAR 文件的名称。 </td></tr><tr><td><b>type</b></td><td>（可选的）相关性的类型；如 jar 和分发版等。缺省值是 jar。</td></tr><tr><td><b>url</b></td><td>（可选的）相关性项目的 URL，在相关性是在因特网上找到的第三方库时非常有用。</td></tr></tbody></table><p><a name="N101C8"><span class="smalltitle"><strong><font face="Arial">资源库</font></strong></span></a></p><p>资源库是另一个主要的 Maven 组件。在有多个项目的基于 Java 的站点中，由第三方库组成的中央资源库常常确保项目之间的一致性。Maven 使资源库的结构符合标准，并且支持驻留在因特网或内部网上的远程资源库。清单 5 显示了资源库的常规结构。</p><br /><a name="N101D1"><b>清单 5. 资源库</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">repository
|-- ant                   &lt;-- project group ID --&gt;
|   `-- jars              &lt;-- artifact type, followed by 's', 
|                         &lt;-- e.g. jars, wars, ears --&gt;
|       `-- ant-1.5.1.jar &lt;-- actual artifact --&gt;
...
</font></code></pre></td></tr></tbody></table><br /><p>要创建远程资源库，只需将这个资源库的目录部署在网站中。Maven 建议使用远程资源库以便于集中维护，您将会最大程度地实现项目之间资源的共享。为避免每次构建时都要下载文件，Maven 在首次下载必需的相关性资源时就自动地将其高速缓存在本地资源库中。Maven 将表 3 中所示的特性用于远程资源库和本地资源库。</p><p><a name=" "><span class="smalltitle"><strong><font face="Arial">表 3. 用于远程资源库和本地资源库的特性</font></strong></span></a></p><p></p><table cellspacing="0" cellpadding="3" width="100%" border="1"><tbody><tr><td><code>maven.repo.remote</code></td><td>用以逗号分隔的 URL 列表指定远程资源库；缺省情况下使用 http://www.ibiblio.org/maven。</td></tr><tr><td><code>maven.proxy.host</code> 、 <code>maven.proxy.port</code> 、 <code>maven.proxy.username</code> 和 <code>maven.proxy.password</code></td><td>如果位于防火墙后面并且需要代理认证才能访问因特网，这些设置将派上用场。</td></tr><tr><td><code>maven.repo.local</code></td><td>指定已下载的相关资源的高速缓存位置，缺省值为 <code>${MAVEN_HOME}/repository</code> 。在 UNIX 环境中，为了与多个团队共享资源库目录，可以为开发人员创建一个特殊的组，然后给予这个组对资源库目录的读／写访问权。 </td></tr></tbody></table><table cellspacing="0" cellpadding="0" width="40%" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td><table cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td bgcolor="#eeeeee"><a name="N10227"><b>Maven 中的 Ant 任务</b></a><br /><p>Maven 中的 goal 可在其定义中包含任何有效的 Ant 任务，这一点有助于您快速掌握 Maven 以及保护您的 Ant 投入。</p></td></tr></tbody></table></td></tr></tbody></table><p><a name="N10231"><span class="smalltitle"><strong><font face="Arial">goal</font></strong></span></a></p><p>Maven 中的 goal 类似 Ant 中的 <code>target</code> 。两者都包含实现 goal（或 target）时会执行的任务。要在命令行中实现特定的 goal，可输入 <code>maven &lt;goal&gt;</code> 。 </p><p>要列出所有已定义的 goal，可使用 <code>maven -g</code> 。表 4 列出了常用的 goal。 </p><p><a name=" "><span class="smalltitle"><strong><font face="Arial">表 4. 常用的 goal</font></strong></span></a></p><p></p><table cellspacing="0" cellpadding="3" width="100%" border="1"><tbody><tr><td><code>java:compile</code></td><td>编译所有 Java 源代码。</td></tr><tr><td><code>jar</code></td><td>创建已编译的源代码的 JAR 文件。</td></tr><tr><td><code>jar:install</code></td><td>将已创建的 JAR 文件发布到本地资源库，使得其它项目可访问该 JAR 文件。</td></tr><tr><td><code>site</code></td><td>创建项目站点文档。缺省站点文档包含关于项目的有用信息，如包／类相关性、编码风格一致性、源代码交叉引用、单元测试结果或 Javadoc。要生成的报告列表是可定制的。</td></tr><tr><td><code>site:deploy</code></td><td>部署生成的站点文档。</td></tr></tbody></table><p>Maven 的 goal 是可扩展和可重用的。知道了这一点后，在编写自己的 goal 之前，可先在 Maven 站点上或 <code>${MAVEN_HOME}/plugins</code> 中查看 Maven 插件列表。另一个关于免费 Maven 插件的较佳资源是 SourceForge 上的 Maven 插件项目。（以上各项的链接可在 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>中获得）。 </p><p>如果仍不能找到符合您要求的 goal，Maven 给您两种选择：</p><ul><li>编写 <code>&lt;preGoal&gt;</code> 或 <code>&lt;postGoal&gt;</code> 来扩展标准 goal 
</li><li>编写自己的 goal </li></ul><p>无论选择哪种，都要在项目目录中创建名为 maven.xml 的特殊文件。清单 6 显示了框架 maven.xml。</p><br /><a name="N102BC"><b>清单 6. 框架 maven.xml</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">      &lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
      &lt;project xmlns:j="jelly:core"&gt;
        ...
        &lt;goal name=...&gt;
          ... build rules, e.g.
          &lt;mkdir dir="${test.result.dir}"/&gt;
          &lt;echo&gt;Executing JUnit tests&lt;/echo&gt;
          ...
        &lt;/goal&gt;
        ...
        &lt;preGoal name=...&gt;
          ...
        &lt;/preGoal&gt;

        &lt;postGoal name=...&gt;
          ...
        &lt;/postGoal&gt;

      &lt;/project&gt;
</font></code></pre></td></tr></tbody></table><br /><p>熟悉 Ant 的开发人员会发现 Maven 的 goal（同样还有 <code>preGoal</code> 和 <code>postGoal</code> ）可在其定义中包含任何有效的 Ant 任务，这有助于快速学习 Maven 并保护在 Ant 上的投入。为了给 Ant 任务添加动态性，Maven 也使用 Jelly 脚本编制语言。“ <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/sidefile1.shtml"><font color="#5c81a7">基础 Jelly 编程</font></a>”用一个样本 maven.xml 文件介绍 Jelly 脚本编制语言。 </p><p><b>编写 &lt;preGoal&gt; 和 &lt;postGoal&gt;</b><br />Ant 的 <code>&lt;target&gt;</code> 与 <code>makefile</code> 规则的相似之处在于：定义了规则以后，前提条件和后置条件是固定的。这使得在多个项目间重用构建规则变得更加困难。例如，某个项目中的 <code>compile</code> target 可能依靠 XDoclet 生成源文件，而另一个 <code>compile</code> target 可能不包括任何先决条件。为了克服这种限制，Maven 提供了两个特殊标记： <code>&lt;preGoal&gt;</code> 和 <code>&lt;postGoal&gt;</code> 。从标记的名称可以看出： <code>preGoal</code> 定义在指定的 goal 之前执行的构建规则。另一方面， <code>postGoal</code> 定义实现指定 goal 之后要执行的构建规则。例如，清单 7 中的 <code>preGoal</code> 指示 Maven 在编译源代码之前用 XDoclet 生成源文件。 </p><br /><a name="N10301"><b>清单 7. 样本 preGoal 部分</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">        &lt;preGoal name="java:compile"&gt;
          &lt;attainGoal name="xdoclet:ejbdoclet"/&gt;
        &lt;/preGoal&gt;
</font></code></pre></td></tr></tbody></table><br /><p>Maven 还提供与 Ant 的 <code>&lt;antcall&gt;</code> 标记相似的 <code>&lt;attainGoal&gt;</code> 标记，以便在确有必要直接实现 goal 的情况（如上例）下使用。 </p><p><b>编写自己的 goal</b><br />如果 goal 是特定于项目的，则可在 maven.xml 文件中定义自己的 goal。这些自定义的 goal 会覆盖其它同名的 goal。如果项目包括子项目，子项目也继承这些 goal。 </p><p><b>编写插件</b><br />为了在项目间共享 goal，可在 Maven 安装插件目录（ <code>${MAVEN_HOME}/plugins</code> ）中将其打包为插件。典型的 Maven 插件包含 project.xml 和 plugin.jelly 文件。project.xml 文件描述插件的 POM；plugin.jelly 类似 maven.xml 且包含该插件所展示的 goal。插件可以有自己的资源和相关性信息。预先定义的变量 <code>${plugin.dir}</code> 让用户引用插件目录中的资源。例如，在清单 8 中所示的插件结构中， <code>${plugin.dir}/dtd/web-app_2_3.dtd</code> 可访问 <code>plugin.jelly</code> 中的 <code>web-app_2_3.dtd</code> 。 </p><br /><a name="N1033A"><b>清单 8. 样本插件结构</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">    ejbjar-plugin-1.0
    |-- dtd
    |   |-- application_1_3.dtd
    |   |-- ejb-jar_2_0.dtd
    |   |-- web-app_2_3.dtd
    |-- plugin.jelly
    `-- project.xml
</font></code></pre></td></tr></tbody></table><br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></font></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><font face="Lucida Console"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></font></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="3"><span class="atitle"><font face="Arial" size="4">安装 Maven</font></span></a></p><p>最近发行的 Maven 1.0-beta-8 基本上是 1.0 的功能完善版。因为 Maven 开发社区每天都在修正错误，如果您遇到任何无法正常工作的问题，则立即从 CVS（Concurrent Version System，并发版本控制系统）获得最新 Maven 版本，然后自行构建（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>以获得指示信息）。下载了最新的 Maven 源代码之后，可通过调用以下命令来构建 Maven： </p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">      ant -f build-bootstrap.xml

      (set MAVEN_HOME to where you want Maven to reside and
      use Ant 1.5.1 to perform the build)
</font></code></pre></td></tr></tbody></table><br /><p>如果在防火墙之后操作，请正确设置以下特性： <code>maven.proxy.host</code> 、 <code>maven.proxy.port</code> 、 <code>maven.proxy.username</code> 和 <code>maven.proxy.password</code> 。缺省情况下，Maven 资源库驻留在 <code>${MAVEN_HOME}/repository</code> 中；通过将 <code>maven.repo.local</code> 特性设置为新位置，可以更改 Maven 资源库的位置。 </p><table cellspacing="0" cellpadding="0" width="40%" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td><td><table cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td bgcolor="#eeeeee"><a name="N10372"><b>样本项目文件</b></a><br /><p>请参阅在这个样本 J2EE 项目中使用的 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/sidefile2.shtml"><font color="#5c81a7">Maven 项目文件</font></a>。 </p></td></tr></tbody></table></td></tr></tbody></table><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="4"><span class="atitle"><font face="Arial" size="4">样本 J2EE 项目</font></span></a></p><p>掌握了到目前为止所学的知识后，就可以着手使用 Maven 了。本节描述如何用 Maven 设置一个样本 J2EE 项目。</p><p><a name="N10389"><span class="smalltitle"><strong><font face="Arial">项目目录布局</font></strong></span></a></p><p>在进行详细介绍之前，我先说明一下项目的目录布局。尽管不作要求，但事实证明一致的跨项目目录布局非常有用，因为熟悉了一个项目的开发人员可以轻松地浏览其它项目。更重要的是，一致的目录布局可让您编写通用的构建规则。</p><p>Maven 的目录布局指南（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>）适用于绝大多数项目。作为演示，我使用略微不同的布局，如清单 9 所示。 </p><br /><a name="N10399"><b>清单 9. 样本项目目录布局</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> project
 |
 |-- LICENSE.txt
 |-- project.properties
 |-- maven.xml
 |-- project.xml
 |-- src
 |   `-- java
 |       `-- com/....
 |   `-- conf
 |       `-- Configuration files for staging environments.
 |-- test
 |   `-- java
 |       `-- com/....
 |   `-- conf
 |       `-- Configuration files for unit testing environments.
 `-- xdocs
     `-- index.xml
</font></code></pre></td></tr></tbody></table><br /><p>一个 J2EE 项目通常生成 WAR 文件、EJB JAR 文件和 EAR 文件。因为每种文件都包括自己的相关性信息和源文件，所以应将其作为单独项目来构建。通常，通过将子项目存储为主项目的子目录，来构造这一项目／子项目关系。我们的布局如清单 10 所示。</p><br /><a name="N103A6"><b>清单 10. Maven 中 J2EE 项目的高级目录布局</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">j2ee-project
|
|-- project.xml       - Produces the EAR file
|
|-- util-subproject
|   |
|   `-- project.xml   - Produces the Utility JAR file
|
|-- ejb-subproject
|   |
|   `-- project.xml   - Produces the EJB JAR file
|
`-- web-subproject
    |
    `-- project.xml   - Produces the WAR file
</font></code></pre></td></tr></tbody></table><br /><p><a name="N103B0"><span class="smalltitle"><strong><font face="Arial">项目继承</font></strong></span></a></p><p>项目继承让 POM 以类似于对象继承的方式从主 POM 继承 ― 由于这些项目之间的细微差别（主要是相关性的差别），这一特性在此尤为重要。项目管理部分可在主 project.xml 中集中维护。要使用项目继承，可使用 project.xml 中的 <code>&lt;extend&gt;</code> 标记（请参阅“ <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/sidefile2.shtml"><font color="#5c81a7">样本项目文件</font></a>”中的清单 2）。 </p><p><a name="N103C1"><span class="smalltitle"><strong><font face="Arial">样本项目的 goal</font></strong></span></a></p><p>既然已经定义了 POM，就可以编写它们的 goal。因为这些 goal 使用 POM 中定义的特性，所以在继续之前应该首先理解“ <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/sidefile2.shtml"><font color="#5c81a7">样本项目文件</font></a>”中的 project.xml 文件。 </p><p><a name="N103CE"><span class="smalltitle"><strong><font face="Arial">Utility 子项目</font></strong></span></a></p><p>由于 Utility 子项目生成一个包含源目录中类的 JAR 文件 ― 由缺省的 <code>jar:jar</code> goal 即可满足要求，因此这里不需要定制的 goal。 </p><p>因为 Web 子项目和 EJB 子项目都依靠 Utility 子项目，所以，在构建 Web 子项目和 EJB 子项目之前，应该调用 <code>jar:install</code> goal 以将 Utility 子项目 JAR 文件部署到本地资源库。这样，WAR 子项目和 EJB 子项目就可以正确地解析相关性。 </p><p><a name="N103E2"><span class="smalltitle"><strong><font face="Arial">Web 子项目</font></strong></span></a></p><p>Web 子项目生成一个 WAR 文件，该文件包含源目录的类、 <code>jsp</code> 目录中的 JSP 文件和 <code>conf</code> 目录中的 web.xml 文件。缺省 <code>war:war</code> goal 有更简单的关于项目目录布局的视图。要重用该 goal，可如下定制其行为： </p><ol><li>在项目的 project.properties 文件中，将特性 <code>maven.war.src</code> 和 <code>maven.war.webxml</code> 分别设置为 <code>${maven.build.dir}/webapp</code> 和 <code>${maven.src.dir}/conf/web.xml</code> 。这告诉 <code>war:war</code> 在哪里查找 Web 来源（JSP 页面、HTML 静态页面和图像等）和 web.xml 文件。 <br /></li><li>定义一个将所有 JSP 文件复制到 <code>${maven.build.dir}/webapp</code> 目录中的 <code>preGoal</code> 。以下 maven.xml 可实现这一效果： 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;project&gt;

  &lt;preGoal name="war:init"&gt;
    &lt;copy todir="${maven.build.dir}/webapp"&gt;
      &lt;fileset dir="${maven.src.dir}/jsp" include="*.jsp"/&gt;
    &lt;/copy&gt;
  &lt;/preGoal&gt;

&lt;/project&gt;
</font></code></pre></td></tr></tbody></table><br /></li></ol><p>当调用 <code>war:war</code> goal 时，请注意 Utility JAR 文件和 commons-beanutils JAR 文件都被打包到 WAR 文件。通过查看 project.xml 文件的相关性部分中的 <code>war.bundle.jar</code> 特性，Maven 知道要在 WAR 文件中包括哪个文件。 </p><p><a name="N10430"><span class="smalltitle"><strong><font face="Arial">EJB 子项目</font></strong></span></a></p><p>给 EJB JAR 文件打包和给 JAR 文件打包相似。如果项目设置与缺省 <code>ejb</code> goal 不匹配，可应用以上“Web 子项目”一节中所描述的技术。在这个特定例子中，将 ejb-jar.xml 从 <code>conf</code> 目录复制到 <code>${maven.build.dir}/ejb/META-INF</code> 目录，并将 <code>maven.ejb.src</code> 特性设置为 <code>${maven.build.dir}/ejb</code> 。 </p><p>要将相关性 JAR 文件添加到 EJB JAR 的清单类路径（manifest classpath）中，可在相关性部分中使用 <code>ejb.manifest.classpath</code> 特性。 </p><p><a name="N10454"><span class="smalltitle"><strong><font face="Arial">主（EAR）项目</font></strong></span></a></p><p>在成功编译并部署了子项目（使用 <code>jar:install</code> 、 <code>war:install</code> 和 <code>ejb:install</code> goal）之后，即可创建最终的 EAR 文件。相关性特性 <code>ear.bundle.jar</code> 、 <code>ear.bundle.ejb</code> 和 <code>ear.bundle.war</code> 告诉 <code>ear</code> 插件要在 EAR 文件中包括哪些文件。（对于 Maven 1.0-beta-8，WAR 文件不是受支持的相关性类型，因此 EAR 插件不能正确地给 WAR 文件打包。解决办法：使用 <code>postGoal</code> 手工更新 EAR 文件。） </p><p><a name="N1047D"><span class="smalltitle"><strong><font face="Arial">reactor：自动构建子项目</font></strong></span></a></p><p>构建 J2EE 项目需要大量的工作。事实证明，每次项目更改时重复同样的过程耗费时间而且容易出错。为帮助解决这些问题，Maven 的 reactor 功能部件以正确的顺序自动构建子项目，这样就节省了时间且减少了错误。</p><p>清单 11 的 maven.xml 演示了定义 reactor 的方法。</p><br /><a name="N10489"><b>清单 11. 样本 reactor 定义</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;project default="all"
         xmlns:m="jelly:maven"&gt;

  &lt;goal name="all"&gt;
    &lt;m:reactor basedir="${basedir}"
               includes="*/project.xml"
               goals="install"
               banner="Building"
               ignoreFailures="false"/&gt;
  &lt;/goal&gt;
&lt;/project&gt;
</font></code></pre></td></tr></tbody></table><br /><p>该 reactor 首先在 <code>basedir</code> 目录下搜索 project.xml 文件，然后调用 <code>install</code> goal。执行的顺序取决于每个项目中的相关性部分。此外，通常可以在主项目的 maven.xml 文件中定义 reactor。因为 goal 在子项目中继承，所以选择 goal 的名称时要当心。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="5"><span class="atitle"><font face="Arial" size="4">参与其中</font></span></a></p><p>尽管 Maven 是功能丰富的产品，但它仍处于 beta 测试版阶段。因此错误可能在任何地方突然出现。别慌。要找到解决错误的答案，最好的办法是搜索 Maven 的邮件列表归档（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>）以了解相关的通告。如果没有任何发现，试着将问题公布到邮件列表，以便得到别人的建议。邮件列表上列出的人一般都乐于给予帮助。 </p><p>要正式地报告错误，请访问 Maven 项目的问题跟踪系统（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#resources"><font color="#996699">参考资料</font></a>）。 </p><p>一旦熟悉了 Maven，您可能会在插件的实现中找到绝大多数答案。当您达到专家水平并且相信 Maven 大有前途的时候，请向社区提供补丁，帮助 Maven 成长。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="6"><span class="atitle"><font face="Arial" size="4">结束语</font></span></a></p><p>随着如今的项目变得越来越复杂，我们需要能帮助我们表示并管理这些复杂性的工具。Maven 将项目对象模型与功能强大的 XML 脚本编制语言相结合，为我们提供了这样的工具。在本文中，您已经了解如何定义 POM 以及如何使用 Maven 的 <code>goal</code> 机制构建项目。我们还研究了使用 Jelly 定制构建行为的不同方法。最后，利用一个样本 J2EE 项目，我们将概念在实际中加以应用。希望您下载 Maven 以推动其发展。 </p><p><i>作者感谢 Jason van Zyl 对本文的评审。</i></p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="resources"><span class="atitle"><font face="Arial" size="4">参考资料 </font></span></a></p><ul><li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/j-maven/index.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li><li>有关 Maven 及其各种插件的更多信息，请访问 <a href="http://maven.apache.org/"><font color="#5c81a7">Maven</font></a>网站。您将在那里发现有用的参考资料，包括： 
<ul><li>推荐的 <a href="http://maven.apache.org/reference/dirlayout.html"><font color="#5c81a7">目录布局</font></a></li><li><a href="http://maven.apache.org/reference/plugins/index.html"><font color="#5c81a7">内置插件文档</font></a></li><li><a href="http://maven.apache.org/mail-lists.html"><font color="#5c81a7">邮件列表</font></a></li><li>使用 Maven 的 <a href="http://maven.apache.org/powered.html"><font color="#5c81a7">项目列表</font></a></li><li>Maven <a href="http://maven.apache.org/cvs-usage.html"><font color="#5c81a7">CVS 资源库</font></a></li><li><a href="http://maven.apache.org/reference/user-guide.html"><font color="#5c81a7">Maven User Guide</font></a>，针对 1.0-beta-8 更新 </li></ul><br /><br /></li><li>Sourceforge.net 的 <a href="http://maven-plugins.sourceforge.net/"><font color="#5c81a7">Maven 插件</font></a>项目提供免费 Maven 插件。 <br /><br /><br /></li><li>Maven 的 <a href="http://jira.werken.com/BrowseProject.jspa?id=10030"><font color="#5c81a7">问题跟踪系统</font></a>可用于报告和查询错误。 <br /><br /><br /></li><li><a href="http://jakarta.apache.org/commons/sandbox/jelly/"><font color="#5c81a7">Jelly 网站</font></a>对 Jelly 标记的出色引用是该站点的一大亮点。 <br /><br /><br /></li><li>Maven 的 <a href="http://maven.apache.org/apidocs/index.html"><font color="#5c81a7">API 文档</font></a>在您使用 Jelly 编写构建规则时会非常有用。 <br /><br /><br /></li><li><a href="http://ant.apache.org/"><font color="#5c81a7">Ant 网站</font></a>提供对 Ant 任务的出色引用。在 Maven 中可使用绝大多数 Ant 任务。 <br /><br /><br /></li><li>教程“ <a href="http://www.sitepen.com/ant/javascript.html"><font color="#5c81a7">Using JavaScript with Ant</font></a>”向您演示如何用 JavaScript 编写动态 Ant 任务。 <br /><br /><br /></li><li><a href="http://krysalis.org/centipede/"><font color="#5c81a7">Krysalis Centipede</font></a>是另一个基于 Ant 的构建系统。 <br /><br /><br /></li><li>在“ <a href="http://www.ibm.com/developerworks/java/library/j-antbuild/"><font color="#5c81a7">Extending Ant to support interactive builds</font></a>”（ <i>developerWorks</i>，2001 年 11 月）中，Anthony Young-Garner 向您演示如何在构建中加入交互式支持，以便为最终用户提供更顺畅、更灵活的体验。 <br /><br /><br /></li><li>Malcolm Davis 撰写的“ <a href="http://www-128.ibm.com/developerworks/cn/java/j-ant/"><font color="#5c81a7">利用 Ant 和 JUnit 进行增量开发</font></a>”（ <i>developerWorks</i>，2000 年 11 月）建议用单元测试改进代码。 <br /><br /><br /></li><li><a href="http://jakarta.apache.org/gump/"><font color="#5c81a7">GUMP</font></a>是连续集成相互从属的项目的工具。 <br /><br /><br /></li><li>“ <a href="http://www7.software.ibm.com/vad.nsf/Data/Document4366&amp;origin=j"><font color="#5c81a7">Automated builds with VisualAge for Java and Ant</font></a>”（ <a href="http://www-128.ibm.com/developerworks/cn/wsdd/"><font color="#5c81a7"><i>WebSphere 开发者园地</i></font></a>，2001 年 1 月）介绍如何为项目创建自动化构建。 <br /><br /><br /></li><li>您将在 <a href="http://www-128.ibm.com/developerworks/cn/java/"><font color="#5c81a7"><i>developerWorks</i>Java 技术专区 </font></a>找到涉及 Java 编程各个方面的数百篇文章。 <br /><br /></li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-maven/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="author"><span class="atitle"><font face="Arial" size="4">关于作者</font></span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="2"><font face="Arial" size="4"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></font></td></tr><tr valign="top" align="left"><td><p><font face="Arial" size="4"></font></p></td><td><p>Charles Chan 是 Finetix LLC 的一名顾问。Charles 的兴趣包括分布式系统、高性能计算、国际化和软件设计模式。在业余时间，他为开放源码社区撰稿。可以通过 <a href="mailto:charlesc@ibiblio.org"><font color="#5c81a7">charlesc@ibiblio.org</font></a> 与 Charles 联系。 </p></td></tr></tbody></table><img src ="http://www.blogjava.net/TrampEagle/aggbug/30036.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-09 14:52 <a href="http://www.blogjava.net/TrampEagle/articles/30036.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>