﻿<?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-世事如棋-文章分类-Java Server Faces </title><link>http://www.blogjava.net/kingwell/category/8831.html</link><description>Aspire to Professionalism</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 07:48:18 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 07:48:18 GMT</pubDate><ttl>60</ttl><item><title>velocity简介</title><link>http://www.blogjava.net/kingwell/articles/50307.html</link><dc:creator>KingWell</dc:creator><author>KingWell</author><pubDate>Sun, 04 Jun 2006 10:51:00 GMT</pubDate><guid>http://www.blogjava.net/kingwell/articles/50307.html</guid><wfw:comment>http://www.blogjava.net/kingwell/comments/50307.html</wfw:comment><comments>http://www.blogjava.net/kingwell/articles/50307.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingwell/comments/commentRss/50307.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingwell/services/trackbacks/50307.html</trackback:ping><description><![CDATA[Velocity是什么？<br />Velocity是一个基于java的模板引擎（template engine）。它允许任何人仅仅简单的使用模板语言（template language）来引用由java代码定义的对象。<br />当Velocity应用于web开发时，界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点，也就是说，页面设计人员可以只关注页面的显示效果，而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来，这样为web站点的长期维护提供了便利，同时也为我们在JSP和PHP之外又提供了一种可选的方案。<br />Velocity的能力远不止web站点开发这个领域，例如，它可以从模板（template）产生SQL和PostScript、XML，它也可以被当作一个独立工具来产生源代码和报告，或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务（template service）。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。<br /><br />Velocity能为我们作什么？<br />The Mud Store Example <br />假设你是一家专门出售Mud的在线商店的页面设计人员，让我们暂且称它为“在线MUD商店”。你们的业务很旺，客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站，登陆后就允许他们查看订单并购买更多的mud。现在，一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud，由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的，所以某天有一个问题可能会冒出来：为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢？<br />Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员，你希望每个用户登陆时都拥有自己的页面。<br />你会见了一些公司内的软件工程师，你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边，看一看你应该作些什么吧。<br />你可能在页面内嵌套如下的VTL声明：<br />&lt;html&gt;<br />&lt;body&gt;<br />Hello $customer.Name!<br />&lt;table&gt;<br />#foreach( $mud in $nudsOnSpecial ) <br />#if ( $customer.hasPurchased( $mud ) ) <br />&lt;tr&gt;&lt;td&gt;$flogger.getPromo( $mud )&lt;/td&gt;&lt;/tr&gt;<br />#end<br />#end<br />&lt;/table&gt;<br /><br />Velocity Template Language(VTL):AN introduction<br />VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。<br />VTL使用references来在web site内嵌套动态内容，一个变量就是一种类型的reference。变量是某种类型的refreence，它可以指向java代码中的定义，或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子，它可以被嵌套到HTML代码中：<br />  #set ( $a = “Velocity” )<br />和所有的VTL statement一样，这个statement以＃字符开始并且包含一个directive：set。当一个在线用户请求你的页面时，Velocity Templating Engine将查询整个页面以便发现所有＃字符，然后确定哪些是VTL statement，哪些不需要VTL作任何事情。<br />＃字符后紧跟一个directive：set时，这个set directive使用一个表达式（使用括号封闭）――一个方程式分配一个值给变量。变量被列在左边，而它的值被列在右边，最后他们之间使用＝号分割。<br />在上面的例子中，变量是$a，而它的值是Velocity。和其他的references一样以$字符开始，而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。<br />记住以下的规则：<br />使用$字符开始的references用于得到什么；使用#字符开始的directives用于作些什么。<br />Hello Velocity World!<br />一旦某个变量被分配了一个值，那么你就可以在HTML文件的任何地方引用它。在下面的例子中，一个值被分配给$foo变量，并在其后被引用。<br />&lt;html&gt;<br />&lt;body&gt;<br />#set ( $foo = “Velocity” )<br />Hello $foo World!<br />&lt;/body&gt;<br />&lt;/html&gt;<br />上面的实现结果是在页面上打印“Hello Velocity World！”<br />为了使包含VTL directives的statement更具有可读性，我们鼓励你在新行开始每个VTL statement，尽管你不是必须这么作。Set directive将在后面详细描述。<br />注释<br />单行注释：<br />  ## This is a single line comment.<br />多行注释：<br />  #*<br />   Thus begins a multi-line comment. Online visitors won’t<br />   see this text because the Velocity Templating Engine will<br />ignore it.<br />  *#<br />文档格式：<br />  #**<br />   This is a VTL comment block and<br />   may be used to store such information<br />as the document author and versioning<br />   information:<br />   @version 5<br />   @author<br />  *#<br /><br />References<br />在VTL中有三种类型的references：变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者，你和你的工程师必须就references的名称达成共识，以便你可以在你的template中使用它们。<br />Everything coming to and from a reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象，那么Velocity将调用它的toString()方法将这个对象转型为String类型。<br />  变量 <br />  格式要求同java。<br />  属性 <br />  例子：<br />    $customer.Address<br />    $purchase.Total<br />$customer.Address有两种含义。它可以表示：查找hashtable对象customer中以Address为关键字的值；也可以表示调用customer对象的getAddress()方法。当你的页面被请求时，Velocity将确定以上两种方式选用那种，然后返回适当的值。<br />方法 <br />一个方法就是被定义在java中的一段代码，并且它有完成某些有用工作的能力，例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References，一般还包括一个VTL方法体。例如：<br />  $customer.getAddress()<br />  $purchase.getTotal()<br />  $page.setTitle( “My Home Page” )<br />  $person.setAttributes( [“Strange”, “Weird”, “Excited”] )<br />前两个例子$customer.getAddress()和$purchase.getTotal()看起来挺想上面的属性$customer.Address 和 $purchase.Total。如果你觉得他们之间有某种联系的话，那你是正确的。<br />VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。<br />正式reference标记<br />reference的正是格式如下：<br />  ${mudSlinger}        变量<br />  ${customer.Address}    属性<br />  ${purchase.getTotal()}    方法<br />非正是格式更见常用，但是有时还是使用正是格式比较适合。例如：你希望通过一个变量$vice来动态的组织一个字符串。<br />  Jack is a $vicemaniac.<br />本来变量是$vice现在却变成了$vicemaniac，这样Veloctiy就不知道您到底要什么了。所以，应该使用正是格式书写<br />  Jack is a ${vice}maniac<br />现在Velocity知道变量是$vice而不是$vicemaniac。<br />Quiet reference notation<br />例如：<br />  &lt;input type=”text” name=”email” value=”$email” /&gt;<br />当页面的form被初始加载时，变量$email还没有值，这时你肯定是希望它能够显示一个blank text来代替输出”$email”这样的字段。那么使用quiet reference notation就比较合适。<br />  &lt;input type=”text” name=”email” value=”$!email”/&gt;<br />这样文本框的初始值就不会是email而是空值了。<br />正式和quiet格式的reference notation也可一同使用，像下面这样：<br />  &lt;input type=”text” name=”email” value=”$!{email}”/&gt;<br />Getting literal <br />Velocity使用特殊字符$和#来帮助它工作，所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。<br />  货币字符 <br />在VTL中使用$2.5这样的货币标识是没有问题得的，VTL不会将它错认为是一个reference，因为VTL中的reference总是以一个大写或者小写的字母开始。<br />Escaping valid VTL reference <br />VTL中使用“\”作为逃逸符。<br />例如：<br />  #set( $email = “foo” )<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />将render为：<br />  foo<br />  $email<br />  \foo<br />  \\$email<br />如果email变量没有被定义则<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />将被render为：<br />  $email<br />  \$email<br />  \\$email<br />  \\\$email<br />注意：VTL中未被定义的变量将被认为是一个字符串，所以以下例子：<br />  #set( $foo = “gibbous” )<br />  $moon = $foo<br />的输出结果是：<br />$moon = gibbous<br />Case substitution<br />现在你已经对reference比较熟悉了，你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如：<br />  $foo<br />  $foo.getBar()<br />  ## is the same as<br />  $foo.Bar<br /><br />  $data.getUser(“jon”)<br />  ## is the same as<br />  $data.User(“jon”)<br /><br />  $data.getRequest().getServerName()<br />  # is the same as<br />  $data.Request.ServerName<br />  ## is the same as<br />  ${data.Request.ServerName}<br />但是，注意VTL中不会将reference解释为对象的实例变量。例如：$foo.Name将被解释为Foo对象的getName（）方法，而不是Foo对象的Name实例变量。<br />Directives<br />Reference允许设计者使用动态的内容，而directive使得你可以应用java代码来控制你的显示逻辑，从而达到你所期望的显示效果。<br />  #set <br />  #set directive被用于设置一个reference的值。例如：<br />    #set ( $primate = “monkey” )<br />    #set ( $customer.Behavior = $primate )<br />赋值左侧的（LHS）必须是一个变量或者属性reference。右侧（RHS）可以是以下类型中一种：<br />l  变量reference<br />l  String literal<br />l  属性reference<br />l  方法reference<br />l  number literal<br />l  ArrayList<br />下面是应用各种类型的RHS的例子：<br />  ＃set ( $monkey = $bill ) ##变量reference<br />  ＃set ( $monkey.Friend = “monica” ) ##String literal<br />  ＃set ( $monkey.Blame = $whitehouse.Leak )##属性reference<br />  ＃set ( $monkey.Plan = $spindoctor.weave($web) )##方法reference<br />  ＃set ( $monkey.Number = 123 )##Number literal<br />  ＃set ( $monkey.Say = [“Not”, $my, “fault”] )##ArrayList<br />注意：最后一个例子的取值方法为：$monkey.Say.get(0)<br />RHS也可以是一个简单的算术表达式：<br />  #set ( $value = $foo + 1 )<br />  #set ( $value = $bar -1 )<br />#set ( $value = $foo * $bar )<br />#set ( $value = $foo / $bar )<br />如果你的RHS是一个null，VTL的处理将比较特殊：它将指向一个已经存在的reference，这对初学者来讲可能是比较费解的。例如：<br />  #set ( $resut = $query.criteria(“name”) )<br />  The result of the first query is $result<br /><br />  #set ( $resut = $query.criteria(“address”) )<br />  The result of the second query is $result<br />如果$query.criteria(“name”)返回一个“bill”，而$query.criteria(“address”)返回的是null，则显示的结果如下：<br />  The result of the first query is bill<br />  The result of the first query is bill<br />看看下面的例子：<br />  #set( $criteria = ["name", "address"] )<br />#foreach( $criterion in $criteria )<br />#set( $result = $query.criteria($criterion) )<br />  #if( $result )<br />  Query was successful<br />     #end<br />#end<br />在上面的例子中，程序将不能智能的根据$result的值决定查询是否成功。在$result被#set后（added to the context），它不能被设置回null（removed from the context）。打印的结果将显示两次查询结果都成功了，但是实际上有一个查询是失败的。<br />为了解决以上问题我们可以通过预先定义的方式：<br />  #set( $criteria = [“name”, “address”] )<br />  #foreach( $criterion in $criteria )<br />    #set( $result = false )<br />    #set( $result = $query.criteria( $criterion ) )<br />    #if( $result )<br />      Query was successful<br />    #end<br />  #end<br />  String Literals <br />  当你使用#set directive，String literal封闭在一对双引号内。<br />    #set ( $directoryRoot = “www” )<br />    #set ( $templateName = “index.vm” )<br />    #set ( $template = “$directoryRoot/$tempateName” )<br />    $template<br />  上面这段代码的输出结果为：www/index.vm<br />  但是，当string literal被封装在单引号内时，它将不被解析：<br />    #set ( $foo = “bar” )<br />    $foo<br />    #set ( $blargh = ‘$foo’ )<br />  结果：<br />    bar<br />    $foo<br />  上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。<br />条件语句<br />  if/elseif/else <br />当一个web页面被生成时使用Velocity的#if directrive，如果条件成立的话可以在页面内嵌入文字。例如：<br />  #if ( $foo )<br />    &lt;strong&gt;Velocity!&lt;/strong&gt;<br />  #end<br />上例中的条件语句将在以下两种条件下成立：<br />l  $foo是一个boolean型的变量，且它的值为true<br />l  $foo变量的值不为null<br />这里需要注意一点：Velocity context仅仅能够包含对象，所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值，Velocity也会利用内省机制将它转换为一个Boolean的相同值。<br />如果条件成立，那么#if和#end之间的内容将被显示。<br />#elseif和#else元素可以同#if一同使用。例如：<br />  #if( $foo &lt; 10 )<br />    &lt;strong&gt; Go North &lt;/strong&gt;<br />  #elseif( $foo == 10 )<br />    &lt;strong&gt; Go East &lt;/strong&gt;<br />  #elseif( $foo == 6 )<br />    &lt;strong&gt; Go South &lt;/strong&gt;<br />  #else<br />    &lt;strong&gt; Go West &lt;/strong&gt;<br />  #end<br />注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false，但是与java不同它使用“＝＝”来比较两个值，而且velocity要求等号两边的值类型相同。<br />关系、逻辑运算符 <br />Velocity中使用等号操作符判断两个变量的关系。例如：<br />#set ( $foo = “deoxyribonucleic acid” )<br />#set ( $bar = “ribonucleic acid” )<br />#if ( $foo == $foo )<br />  In this case it’s clear they aren’t equivalent.So…<br />#else<br />  They are not equivalent and this will be the output.<br />#end<br /><br />Velocity有AND、OR和NOT逻辑运算符。下面是一些例子：<br />  ## logical AND<br />  #if( $foo &amp;&amp; $bar )<br />    &lt;strong&gt; This AND that &lt;/strong&gt;<br />  #end<br /><br />  ## logical OR<br />  #if ( $foo || $bar )<br />    &lt;strong&gt;This OR That &lt;/strong&gt;<br />  #end<br /><br />  ##logical NOT<br />  #if ( !$foo )<br />    &lt;strong&gt; NOT that &lt;/strong&gt;<br />  #end<br />循环 <br />  Foreach循环 <br />  例子：<br />    &lt;ul&gt;<br />      #foreach ( $product in $allProducts )<br />        &lt;li&gt; $product &lt;/li&gt;<br />      #end<br />    &lt;/ul&gt;<br />  每次循环$allProducts中的一个值都会赋给$product变量。<br />$allProducts可以是一个Vector、Hashtable或者Array。分配给$product的值是一个java对象，并且可以通过变量被引用。例如：如果$product是一个java的Product类，并且这个产品的名字可以通过调用他的getName（）方法得到。<br />现在我们假设$allProducts是一个Hashtable，如果你希望得到它的key应该像下面这样：<br />&lt;ul&gt;<br />#foreach ( $key in $allProducts.keySet() )<br />&lt;li&gt;Key: $key -&gt; Value: $allProducts.get($key) &lt;/li&gt;<br />#end<br />&lt;/ul&gt;<br /><br />Velocity还特别提供了得到循环次数的方法，以便你可以像下面这样作：<br />&lt;table&gt;<br />#foreach ( $customer in $customerList )<br />&lt;tr&gt;&lt;td&gt;$velocityCount&lt;/td&gt;&lt;td&gt;$customer.Name&lt;/td&gt;&lt;/tr&gt;<br />#end<br />&lt;/table&gt;<br />$velocityCount变量的名字是Velocity默认的名字，你也可以通过修改velocity.properties文件来改变它。默认情况下，计数从“1”开始，但是你可以在velocity.properties设置它是从“1”还是从“0”开始。下面就是文件中的配置：<br />  # Default name of loop counter<br />  # variable reference<br />  directive.foreach.counter.name = velocityCount<br /><br />  # Default starting value of the loop<br />  # counter variable reference<br />  directive.foreach.counter.initial.value = 1<br /><br />include<br />#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因，被引入的本地文件只能在TEMPLATE_ROOT目录下。<br />  #inclued ( “one.txt” )<br />如果您需要引入多个文件，可以用逗号分隔就行：<br />  #include ( “one.gif”, “two.txt”, “three.htm” )<br />在括号内可以是文件名，但是更多的时候是使用变量的：<br />  #inclue ( “greetings.txt”, $seasonalstock )<br /><br />parse<br />#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTL并render模板。<br />  #parse( “me.vm” )<br />就像#include，#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是，#parse只能指定单个对象。<br />你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10。#parse是可以递归调用的，例如：如果dofoo.vm包含如下行：<br />  Count down.<br />  #set ( $count = 8 )<br />  #parse ( “parsefoo.vm” )<br />  All done with dofoo.vm!<br />那么在parsefoo.vm模板中，你可以包含如下VTL：<br />  $count<br />  #set ( $count = $count – 1 )<br />  #if ( $count &gt; 0 )<br />    #parse( “parsefoo.vm” )<br />  #else<br />    All done with parsefoo.vm!<br />  #end<br />的显示结果为：<br />  Count down.<br />  8<br />  7<br />  6<br />  5<br />  4<br />  3<br />  2<br />  1<br />  0<br />  All done with parsefoo.vm!<br />All done with dofoo.vm!<br /><br />Stop<br />#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于debug是很有帮助的。<br />  #stop<br /><br />Velocimacros<br />#macro script element允许模板设计者定义一段可重用的VTL template。例如：<br />  #macro ( d )<br />  &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;<br />  #end<br />在上面的例子中Velocimacro被定义为d，然后你就可以在任何VTL directive中以如下方式调用它：<br />  #d()<br />当你的template被调用时，Velocity将用&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;替换为#d()。<br />每个Velocimacro可以拥有任意数量的参数――甚至0个参数，虽然定义时可以随意设置参数数量，但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro，一个参数是color另一个参数是array：<br />  #macro ( tablerows $color $somelist )<br />  #foreach ( $something in $somelist )<br />    &lt;tr&gt;&lt;td bgcolor=$color&gt;$something&lt;/td&lt;/tr&gt;<br />  #end<br />  #end<br />调用#tablerows Velocimacro：<br />  #set ( $greatlakes = [ “Superior”, “Michigan”, “Huron”, “Erie”, “Ontario” ] )<br />  #set ( $color = “blue” )<br />  &lt;table&gt;<br />    #tablerows( $color $greatlakes )<br />  &lt;/table&gt;<br />经过以上的调用将产生如下的显示结果：<br />  &lt;table&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Superior &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Michigan &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Huron &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Erie &lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=” blue”&gt; Ontario &lt;/td&gt;&lt;/tr&gt;<br />  &lt;/table&gt;<br />Velocimacros可以在Velocity模板内实现行内定义（inline），也就意味着同一个web site内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的：它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。<br />上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(在velocity.properties中定义)里，所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用：<br />  #set ( $parts = [ “volva”, “stipe”, “annulus”, “gills”, “pileus” ] )<br />  #set ( $cellbgcol = “#CC00FF” )<br />  &lt;table&gt;<br />    #tablerows( $cellbgcol $parts )<br />  &lt;/table&gt;<br />上面VTL将产生如下的输出：<br />  &lt;table&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; volva &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; stipe &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; annulus &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; gills &lt;/td&lt;/tr&gt;<br />    &lt;tr&gt;&lt;td bgcolor=”#CC00FF”&gt; pileus &lt;/td&lt;/tr&gt;<br />  &lt;/table&gt;<br />  Velocimacro arguments <br />  Velocimacro可以使用以下任何元素作为参数：<br />l  Reference：任何以$开头的reference<br />l  String literal：<br />l  Number literal：<br />l  IntegerRange：[1….3]或者[$foo….$bar]<br />l  对象数组：[“a”,”b”,”c”]<br />l  boolean值：true、false<br />当将一个reference作为参数传递给Velocimacro时，请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro，而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如：<br />  #macro ( callme $a )<br />    $a $a $a<br />  #end<br />  #callme( $foo.bar() )<br />执行的结果是：reference $foo的bar（）方法被执行了三次。<br />如果你不需要这样的特性可以通过以下方法：<br />  #set ( $myval = $foo.bar() )<br />  #callme ( $myval )<br /><br />Velocimacro properties <br />Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide。<br />Velocity.properties文件中的velocimacro.libraary：一个以逗号分隔的模板库列表。默认情况下，velocity查找唯一的一个库：VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。<br />Velocity.properties文件中的velocimacro.permissions.allow.inline属性：有两个可选的值true或者false，通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros。<br />Velocity.properties文件中的<br />velocimacro.permissions.allow.inline.replace.global属性有两个可选值true和false，这个属性允许使用者确定inline的Velocimacro定义是否可以替代全局Velocimacro定义（比如在velocimacro.library属性中指定的文件内定义的Velocimacro）。默认情况下，此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。<br />Velocity.properties文件中的<br />velocimacro.permissions.allow.inline.local.scale属性也是有true和false两个可选值，默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说，如果这个属性设置为true，一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM敲门：a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected。<br />Velocity.properties文件中的velocimacro.context.localscope属性有true和false两个可选值，默认值为false。当设置为true时，任何在Velocimacro内通过#set()对context的修改被认为是针对此velocimacro的本地设置，而不会永久的影响内容。<br />Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时，对于一个Velocimacro的调用将自动检查原始库是否发生了变化，如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果，就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态（file.resource.loader.cache = false）。注意这个属性实际上是针对开发而非产品的。<br />Velocimacro Trivia <br />Velocimacro必须被定义在他们被使用之前。也就是说，你的#macro()声明应该出现在使用Velocimacros之前。<br />特别要注意的是，如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。<br />Escaping VTL directives<br />VTL directives can be escaped with “\”号，使用方式跟VTL的reference使用逃逸符的格式差不多。<br />  ## #include( “a.txt” ) renders as &lt;ontents of a.txt&gt;(注释行)<br />  #include( “a.txt” )<br /><br />  ## \#include( “a.txt” ) renders as \#include( “a.txt” )<br />  \#include( “a.txt” )<br /><br />  ## \\#include ( “a.txt” ) renders as \&lt;contents of a.txt&gt;<br />  \\#include( “a.txt” )<br />在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心（比如在一个if-else-end statement内）。下面是VTL的if-statement的典型应用：<br />  #if ( $jazz )<br />    Vyacheslav Ganelin<br />  #end<br />如果$jazz是ture，输出将是：<br />  Vyacheslav Ganelin<br />如果$jazz是false，将没有输出。使用逃逸符将改变输出。考虑一下下面的情况：<br />  \#if ( $jazz )<br />    Vyacheslav Ganelin<br />  \#end<br />现在无论$jazz是true还是false，输出结果都是：<br />  #if ( $jazz )<br />    Vyacheslav Ganelin<br />  #end<br />事实上，由于你使用了逃逸符，$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的，例如：<br />  \\#if ( $jazz )<br />    Vyacheslav Ganelin<br />  \\#end<br />以上程序的显示结果为：<br />  \ Vyacheslav Ganelin<br />  \<br />但是如果$jazz为false，那么将没有输出。（书上说会没有输出，但是我觉得应该还有有“\”字符被输出。）<br />VTL：Formatting issues<br />尽管在此用户手册中VTL通常都开始一个新行，如下所示：<br />  #set ( $imperial = [ “Munetaka”, “Koreyasu”, “Hisakira”, “Morikune” ] )<br />  #foreach ( $shogun in $imperial )<br />    $shogun<br />  #end<br />但是像下面这种写法也是可以的：<br />  Send me #set($foo = [“$10 and”,”a cake”])#foreach($a in $foo)$a #end please.<br />上面的代码可以被改写为：<br />  Send me<br />  #set ( $foo = [“$10 and “,”a cake”] )<br />  #foreach ( $a in $foo )<br />    $a<br />  #end<br />  please.<br />或者<br />  Send me<br />  #set($foo = [“$10 and “,”a cake”])<br />        #foreach （$a in $foo ）$a<br />      #end please.<br />这两种的输出结构将一样。<br />其他特性和杂项<br />  math   在模板中可以使用Velocity内建的算术函数，如：加、减、乘、除<br />    #set ( $foo = $bar + 3 )<br />    #set ( $foo = $bar - 4 )<br />    #set ( $foo = $bar * 6 )<br />    #set ( $foo = $bar / 2 )<br />  当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到：<br />    #set ( $foo = $bar % 5 )<br />在Velocity内使用数学计算公式时，只能使用像-n,-2,-1,0,1,2,n这样的整数，而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。<br />Range Operator<br />Range operator可以被用于与#set和#foreach statement联合使用。对于处理一个整型数组它是很有用的，Range operator具有以下构造形式：<br />  [n..m]<br />m和n都必须是整型，而m是否大于n则无关紧要。例子：<br />  First example:<br />  #foreach ( $foo in [1..5] )<br />    $foo<br />  #end<br /><br />  Second example:<br />  #foreach ( $bar in [2..-2] )<br />    $bar<br />  #end<br /><br />  Third example:<br />  #set ( $arr = [0..1] )<br />  #foreach ( $i in $arr )<br />    $i<br />  #end<br /><br />  Fourth example:<br />  [1..3]<br />上面四个例子的输出结果为：<br />  First example：<br />  1 2 3 4 5<br /><br />  Second example：<br />  2 1 0 -1 -2<br /><br />  Third example：<br />  0 1<br /><br />  Fourth example：<br />  [1..3]<br />注意：range operator只在#set和#foreach中有效。<br />Advanced Issue：Escaping and！<br />当一个reference被“！”分隔时，并且在它之前有逃逸符时，reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下： <br />#set ( $foo = “bar” )<br />特殊形式  标准格式<br />Render前  Render后  Render前  Render后<br />$\!foo  $!foo  \$foo  \$foo<br />$\!{foo}  $!{foo}  \$!foo  \$!foo<br />$\\!foo  $\!foo  \$!{foo}  \$!{foo}<br />$\\\!foo  $\\!foo  \\$!{foo}  \bar<br />Velocimacro杂记<br />  Can I user a directive or another VM as an argument to a VM? <br />  例如：#center ( #bold( “hello” ) )<br />  不可以。一个directive的参数使用另外一个directive是不合法的。<br />  但是，还是有些事情你可以作的。最简单的方式就是使用双引号：<br />    #set ( $stuff = “#bold( ‘hello’ )” )<br />    #center ( $stuff ) <br />  上面的格式也可以缩写为一行：<br />    #center ( “#bold( ‘hello’ ) )<br />请注意在下面的例子中参数被evaluated在Velocimacro内部，而不是在calling level。例子：<br />  #macro ( inner $foo )<br />    inner : $foo<br />  #end<br /><br />  #macro ( outer $foo )<br />    #set ( $bar = “outerlala” )<br />    outer : $foo<br />  #end<br />  <br />  #set ( $bar = ‘calltimelala’ )<br />  #outer( “#inner($bar)” )<br />输出结果为：<br />  outer : inner : outerlala<br />记住Veloctiy的特性：参数的传递是By Name的。例如：<br />  #macro ( foo $color )<br />    &lt;tr bgcolor = $color &gt;&lt;td&gt;Hi&lt;/td&gt;&lt;/tr&gt;<br />    &lt;tr bgcolor = $color &gt;&lt;td&gt;There&lt;/td&gt;&lt;/tr&gt;<br />  #end<br /><br />  #foo ( $bar.rowColor() )<br />以上代码将导致rowColor()方法两次调用，而不是一次。为了避免这种现象的出现，我们可以按照下面的方式执行：<br />  #set ( $color = $bar.rowColor() )<br />  #foo ( $color )<br />can I register velocimacros via #parse()? <br />目前，Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。<br />如果你试图#parse()一个包含#macro() directive的模板，这一点是需要牢记的。因为#parse()发生在运行期，但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素，这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果，只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。<br />What is velocimacro autoreloading？ <br />velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false。<br />String concatenation<br />开发人员最常问的问题是我如何作字符拼接？在java中是使用“＋”号来完成的。<br />在VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如：<br />#set ( $size = “Big” )<br />#set ( $name = “Ben” )<br />The clock is $size$name.<br />输出结果将是：The clock is BigBen.。更有趣的情况是：<br />  #set ( $size = “Big” )<br />  #set ( $name = “Ben” )<br />  #set ( $clokc = “$size$name” )<br />  The clock is $clock.<br />上例也会得到同样的结果。最后一个例子，当你希望混合固定字段到你的reference时，你需要使用标准格式：<br />  #set ( $size = “Big” )<br />  #set ( $name = “Ben” )<br />  #set ( $clock = “${size}Tall$name” )<br />  The clock is $clock.<br />输出结果是：The clock is BigTallBen.。使用这种格式主要是为了使得$size不被解释为$sizeTall。<img src ="http://www.blogjava.net/kingwell/aggbug/50307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingwell/" target="_blank">KingWell</a> 2006-06-04 18:51 <a href="http://www.blogjava.net/kingwell/articles/50307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>了解 Struts 框架的全新后代:Shale不是Struts</title><link>http://www.blogjava.net/kingwell/articles/48623.html</link><dc:creator>KingWell</dc:creator><author>KingWell</author><pubDate>Sun, 28 May 2006 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/kingwell/articles/48623.html</guid><wfw:comment>http://www.blogjava.net/kingwell/comments/48623.html</wfw:comment><comments>http://www.blogjava.net/kingwell/articles/48623.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingwell/comments/commentRss/48623.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingwell/services/trackbacks/48623.html</trackback:ping><description><![CDATA[　Shale 不是什么?Shale 不是打包好的、有编制好的文档并经过严格测试的产品，也没有附带自动安装程序和优雅的管理界面。那么 Shale 到底是什么呢?Brett McLaughlin 在本文中将揭开这个 Struts 后代的面纱。在本文中，Brett 解释了 Shale 是什么，Shale 与 Struts 框架的不同之处，以及如何在开发环境中安装和设置它。
<p>　　在过去 5 年间出现的所有 Web 框架中，Jakarta Struts 是 Java™ 开发人员使用得最多的一种框架，因此其后代的问世是一件值得注意的事情。虽然 Shale 还不是最流行的框架，也不是最为人熟悉的框架，但是出自名门的背景仍给人以深刻印象。更令人兴奋的是，Shale 并不仅仅是 Struts 的重大升级和新的发行版:它彻底更新了 Struts 中的很多核心原则，并且加入了 Web 开发中最新的思想。</p><p>　　您将了解到，Shale 与 Struts 的背离是一柄双刃剑。一方面，Shale 是经过精心设计的 Struts 的后代。Shale 的创立者综合考虑了 Struts 的优点和不足，提出可与其前辈媲美的下一代框架。另一方面，正如您很快就可以在这个系列中看到的一样，Shale 是 一种完全不同于 Struts 的框架，其中隐含着很多新的开发工作!</p><p>　　Shale 不仅仅是 Struts 的又一个修正版，它已扩展到超出 Struts 所能达到的高度。它包含 Java Web 程序设计中一些最重要的、最近的开发成果，包括 JSP Standard Tag Library(JSTL)和 JavaServer Faces(JSF)，并建立在这些开发成果之上。Shale 完全应该被看作是与 Struts 不同的一种框架，在这个系列中，我将还 Shale 框架以本来面目。在这个月的文章中，将首先对 Shale 与 Struts 之间的区别作一个概述，然后带您体验安装 Shale 并测试安装情况的步骤。最后，我将给出一些思想，令您能进一步参与到 Shale 项目(它是开放源码的)中，并提供一些相关的信息。整个系列的目的就是要向您展示如何安装 Shale 以及如何使用 Shale 构建和开发项目，同时很少涉及 Shale 的前辈，即 Struts 框架。</p><p>　　<strong>评价 Shale</strong></p><p>　　任何新的 Web 开发框架要想在这个竞争已经很激烈的领域占得一席之地，最好能够经受住巨大压力下的评测。好消息是，Shale 独力经受住了细致的考察。但是，坏消息是，由于 Shale 完全是对 Struts 重新构建的产物，因此必须重新编写和重新测试您所有基于 Struts 的代码，以便实现这些代码。您将花同样多的精力来编写一个新的 Shale 应用程序，或将一个 Struts 应用程序转换成 Shale 应用程序，就好像 Shale 与 Struts 完全无关一样。</p><p>　　所以接下来我们忍不住要问，为什么还要采用 Shale 呢?为了得出答案，我首先解释一下 Shale 的伟大之处 —— 这在很大程度上是由于它的 Struts 血统，但这又不是惟一的原因 —— 然后讨论 Shale 之所以没有 被发布为 Struts 框架的重要修正版的两大原因。这样，您就会更好地理解从 Shale 身上可以得到什么，这将有助于评价使用这种下一代的框架是否值得。</p><p>　　<strong>Struts 血统</strong></p><p>　　Shale 重用了大量的 Struts 代码基，并声称 Struts 是它的 “父” 框架，因此如果您要相信 Shale 的价值，就得相信 Struts 的价值。首先，Struts 作为第一个真正意义上的 Web 开发框架，拥有巨大的价值。据 Shale 和 Struts 网站报道，第一批代码是在 2000 年 6 月提交给 Struts CVS 存储库的，而Struts 1.0 是在 2001 年末才发布的。当很多开发人员正在艰难地使用 JavaServer Pages(JSP)和不断变化的 servlet 规范时，Struts 提供了一种易于使用的 Model 2 方法来构建基于 servlet 和 JSP 的 Web 应用程序。换句话说，Struts 使 Web 开发人员可以开发健壮的 Web 应用程序，而不必精于日志记录、分布式计算、JDBC、Servlet、JSP、JNDI、RMI 和 大量其他的 API 和技术。</p><p>　　接下来，Struts 要做的事情就是保持它的强大性:从写出第一批代码开始，Struts 连续 6 年一直是最流行的 Web 开发框架之一。至今它仍然是人们口中的谈资，笔下的素材，使用得不比任何竞争对手少。由于 Struts 是如此流行，如此长寿，如今它已经有丰富的功能，有良好的文档，被广泛地支持，并且易于使用，在它上面进行开发和管理也很容易。数千名开发人员对 Struts 邮件列表上的问题作出答复，数万名开发人员试用 Struts 并报告问题，这使得这些问题很容易得到修复。</p><p>　　最后，Struts 是不断发展的。很多框架一开始比较强大，然后就停滞不前(商业产品和开放源码项目都存在这样的现象)，而 Struts 总是不断提供新的特性。当您下载 Struts 时，核心发行版中还包含一个健壮的确认引擎(validation engine)，并且 Struts 已经与 JavaServer Faces 集成，拥有广泛的标记库和一个不断发展的 Model 2 架构，其中引入了在分布式 n-层应用程序领域中最新的思想。而且告诉您，Struts 还紧跟程序设计中出现的新模式，例如 IoC(Inversion of Control)。Struts 与 WebWork 和 Spring 框架自然地集成，后两者都是具有最佳血统的、为使用 Web 开发中的新方法提供入口的框架。</p><p>　　简而言之，您很难发现在 Struts 中有做不成 或不支持的事。所以显然我们就要问，为什么 Shale 要另起炉灶呢?为什么 Shale 没有成为 Struts 的下一个版本呢?这有两个主要原因:一个原因与鲜为人知的新软件发行惯例有关，另一个原因则与众所周知的 Struts 根基中的弱点有关。让我们分别来考虑这两个原因。</p><p>　　<strong>发行版辨析</strong></p><p>　　要理解 Shale 为什么没有成为 Struts 的一个新的发行版，首先需要理解关于新软件发行的一两件事。对于一个新的软件发行版，大多数用户首先看的是一组新的特性。版本升级的幅度越大，用户对新特性的期待就越大。因此，如果软件版本从 2.1 升级到 2.2，就应该有一些新的特性，但是如果从版本 2.2 升级到版本 3.0，那么就应该有很多 新特性。这就是为什么当一些大型产品(例如 Microsoft Word)或操作系统(例如 Windows 和 Mac OS X )出了新版本的时候，用户总是对它们很挑剔。对于每一个新的发行版，用户总是期待有更多新的特性。</p><p>　　由于大多数用户一味地将注意力放在特性上，他们没有意识到向后兼容性(backward compatibility)才是真正最有价值的东西。虽然每个人都希望 Excel 中加入新的、很好的选项，希望 Panther 与 iTunes 有更好的集成，希望 Gnome 中对 XUL 有更好的支持，但是如果那些用户现有的程序和文件在新版本下突然不能运行的话，相信他们会尖叫，“这是血腥谋杀”。在这种情况下，新特性毫无价值。</p><p>　　对于 Struts 也一样。一般来说，Struts 的每个新版本都增加了新的特性，同时保持了与之前版本的向后兼容性。此外，新版本的 Struts 还需要支持旧版本的 Java 平台和 Servlet 规范，因为已安装的旧的 Struts 要在这些平台上运行。这两个需求 —— 向后兼容性和对旧版本的 Java API 的支持 —— 对于 Shale 来说已经是一个严重的约束。尤其是，至少就 Java 平台而言，JSF API 已经成为 Web 的中心组件。虽然 Struts 也支持 JSF，但是 Shale 却是完全依赖于 JSF 的 —— 这对于需要维持向后兼容性的 Struts 来说简直是不可能的。</p><p>　　最后，派生出一个像 Shale 这样的新项目，同时继续在 Struts 这种已有的项目上进行开发活动，这样做具有无与伦比的优势。如果 Struts 只是简单地升级到 2.0(或者 3.0 或 4.0)，并在不考虑向后兼容性的情况下实现 Shale，那么对于很多人来说，由此造成的移植工作将是令人痛苦的，可能有人干脆连 Struts 也不再使用了。即使仍然维护更旧的代码基，也难于吸引开发人员花时间来修复 bug，他们也不愿意为一个 “遭到废弃” 的或者 “旧” 版本的软件增加特性。</p><p>　　由于这些原因，让 Shale 成为一个全新的项目，使其建立在一个新的代码基之上，是很有意义的。<br /><strong>Shale 的面向服务架构</strong></p><p>　　Shale 之所以没有成为 Struts 的一个新的发行版，其最后一个原因与逻辑没有关系:而是与该框架将新方法接纳到 Web 开发中的能力有关系。Shale 在很多方面与 Struts 存在不同之处，其中有两点最为突出:</p><ul><li>　　Struts 与 JSF 集成，而 Shale 则是建立在 JSF 之上。 
</li><li>　　Struts 实质上是一个巨大的、复杂的请求处理器;而 Shale 则是一组可以以任何方式进行组合的服务。</li></ul><p>　　Shale 对 JSF 的依赖性具有深远的、令人惊讶的意义，而且大部分情况下是积极的意义。随着这个系列的深入，我将深入研究这些意义。在讨论其他方面之前，有必要对造成 Shale 与 Struts 之间差异的第二个方面进行详细的讨论。</p><p>　　如果您多次使用过 Struts，那么会意识到它很大程度上可以看作一个请求处理器，通过它可以接受请求，并指示框架如何处理请求。您可以指出采取哪种动作，使用什么模型，将哪种视图显示给用户，采用什么验证规则，显示哪种表单 ... Struts 是完全可以配置的。然而，所有这些的核心是一个请求处理器，为每个请求提供服务。这样的处理器是 Struts 中最重要、也是最复杂的部分，因为对于在处理一个请求的过程中涉及的所有工作，它都必须进行处理或托管，在 Struts 代码基中几乎没有哪一部分与这个请求处理器没有关系或不受它的影响。</p><p>　　因此，Struts 基本上难于作出改变。如果想修改处理请求的方式，或改变处理请求过程中各部分的顺序，那么将面临巨大的困难。实际上，不得不重新编写 Struts 代码基。更改请求处理器的行为倒是稍微可行，但是大部分是不容变动的。这是 Shale 试图解决的关键问题之一(如果您需要 Web 框架有那种程度的灵活性的话)。</p><p>　　Shale 没有像 Struts 请求处理器那样的中心组件，它只是一组数量很多的服务。可以自由组合这些服务，每个服务与其他服务之间是松散耦合的。通过一组良好定义的接口(有时候实际上就是 Java 接口类，有时候只是组件之间某种形式的契约)，配置 Shale 的行为很容易。这使得 Shale 是可扩展的、灵活的，甚至是 “聪明的”。很容易更改它，不费吹灰之力就可以扩展它，并可以使它迅速适应新的编程方法。像这样松散地组装组件或服务通常被称作面向服务的架构，或简称 SOA。当然，这听起来有点儿玄乎，不过您大可不必因此而觉得 Shale 不好!</p><p>　　<strong>安装 Shale</strong></p><p>　　可以从逻辑上和概念上理解 Shale 与 Struts 的不同之处，但是要想在脑海里弄清楚这两种伟大的框架有什么不同，则需要亲自动手去实践一番。很自然，每一种 Web 框架首先都需要下载和安装。不过幸好，在这个过程中通常可以了解到很多东西。那些安装和设置起来比较困难的项目和产品，通常也难于配置，难于在它上面进行部署，并且(最坏的情况)难于长久运行。虽然安装过程很难作为评价一个 Web 框架好坏的最可靠手段，但是至少肯定应该成为这个标准的一部分。在这一节中，您将学习手动地安装 Shale，对于一些难点有一定的体会，并了解为了使 Shale 运行，系统上需要些什么东西。</p><p>　　注意，我还提到了 Shale 的 简便安装 选项，但是我强烈建议您至少试一试手动安装，了解它提供的较深层的信息。</p><p>　　<strong>先决条件</strong></p><p>　　Shale 的先决条件和需求相当多。和大多数与 Apache 和 Jakarta 相关的项目一样，Shale 的安装要依赖于一些其他的 Jakarta 项目。下面是为了使 Shale 得以运行所需的所有东西的完整列表:</p><ul><li>　　Java Runtime Environment(JRE)和 Java Development Kit(JDK) 1.4 或更高版本 
</li><li>　　Java Servlet API 2.4 或更高版本 
</li><li>　　JSP 2.0 或更高版本 
</li><li>　　JSF 1.1 或更高版本 
</li><li>　　JSP Standard Tag Library(JSTL) 1.1 或更高版本 
</li><li>　　Jakarta Commons BeanUtils 1.7 或更高版本 
</li><li>　　Jakarta Commons Chain 1.0 或更高版本 
</li><li>　　Jakarta Commons Digester 1.7 或更高版本 
</li><li>　　Apache Logging 1.0.4 或更高版本 
</li><li>　　Apache Ant 1.6.3 或更高版本</li></ul><p>　　Apache Ant 只是在构建 Shale 时要用到，但是无论如何，如果您要进行较多的 Java 开发，那么系统上还是需要(很可能已经有了)一个版本的 Ant。如果想跟踪 Shale 中的 bug，那么需要 FindBugs 0.8.5 或更高版本和 JUnit 3.8.1 或更高版本。由于在第 1 部分中我只是讨论 Shale 的安装和使用，因此您还不必关心 FindBugs 或 JUnit，除非您想早点儿装上这两个项目。</p><p>　　<strong>附件和它们的依赖项</strong></p><p>　　和 Struts 一样，Shale 还有一些附件(在 Shale 中常被称作可选 Shale 组件)，这些附件各自又有其依赖项:</p><ul><li>　　Jakarta Commons Validator 1.2 或更高版本 
</li><li>　　Spring Framework 1.2.2 或更高版本 
</li><li>　　Struts Tiles Framework(独立版本)</li></ul><p>　　如果说这份列表显得有些长并且令人生畏的话，那么没错!Shale 使用大量较低级的库、helper 类、实用程序组件和来自其他项目的类。如果必须逐个安装这些组件，配置 Shale 使用它们，并将所有这些组件组装成可以部署的某种形式的话，即使是最专业的开发人员也会对 Shale 望而生畏。此外，由于 Shale 在它的开发圈子内仍然相当年轻，对于这些依赖项的获得和配置仍然有些稚嫩;然而，Shale 确实是非常可行的，而且不需要花费您想象中那么多的精力。<br /><a class="f1"> </a></p><div class="guanggao"><span id="contentAdv"></span></div><p></p><p>　　首先，如果您正在用某种框架从事某种 Web 开发，那么应该已经有了一些作为依赖项的项目。所以这份列表比它初看上去要容易管理得多。例如，任何 Web 开发人员都应该已经有 JRE 和 JDK，也应该有一个 servlet 引擎，例如 Jakarta Tomcat。如果已经有一个 servlet 引擎，那么也应该有 Servlet API 和 JSP 引擎。另外，大部分 servlet 引擎(至少就当前版本而言)都包括一个 JSTL，并且很多都附带了 JSF。最后，大多数开发人员很可能在他们的机器上已经安装了 Ant。因此，这份列表很快就只剩下这些了:</p><ul><li>　　JSF 1.1 或更高版本 (servlet 引擎中可能已经附带了) 
</li><li>　　JSTL 1.1 或更高版本 (servlet 引擎中可能已经附带了) 
</li><li>　　Jakarta Commons BeanUtils 1.7 或更高版本 
</li><li>　　Jakarta Commons Chain 1.0 或更高版本 
</li><li>　　Jakarta Commons Digester 1.7 或更高版本 
</li><li>　　Apache Logging 1.0.4 或更高版本</li></ul><p>　　考虑到 Tapestry 实际上提供了将它的所有依赖项附带下载的选项，因此 “太多依赖项” 的问题很快就不成为问题了。</p><p>　　现在您已经知道了大概，现在我们来看看下载、安装和配置 Shale 及其依赖项的步骤。</p><p>　　<strong>1. 下载 Shale</strong></p><p>　　要下载 Shale，可访问 Shale 项目主页。您可以看到一个 “Shale Download” 区域，其中有 Shale 框架的每晚构建的链接。单击这个链接，便可以进入如图 1 所示的一个站点:</p><p><a href="http://searchwebservices.techtarget.com.cn/TLimages/picview/?/imagelist/06/19/720u7cy75yls.gif" target="_blank"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" alt="点击放大此图片" src="http://searchwebservices.techtarget.com.cn/imagelist/06/19/720u7cy75yls_s.gif" border="1" /></a></p><p>　　图 1. 来自 Shale CVS 存储库的每晚构建</p><p>　　为了使 Shale 运行，需要下载 “framework” 文件和 “dependencies” 文件。例如，在撰写本文时我下载了以下两个文件:</p><ul><li>　　shale-framework-20060204.zip 
</li><li>　　shale-dependencies-20060204.zip</li></ul><p>　　当然，如果您需要或者更愿意下载 .tar.gz 版本，那么可以不选择 .zip 版本，而选择 .tar.gz 版本。由于 Shale 的开发还在进行中，目前还没有发行的构建，因此您应该尽量下载最近的每晚构建(具有最近的日期)。</p><p>　　下载完这两个文件后，首先解压这两个归档文件。对于核心框架，解压后可以得到一个具有形如 shale-framework-20060204/ 的名称的文件夹;对于 dependencies 归档文件，解压后可以得到一个名为 lib/ 的文件夹。将核心框架目录 shale-framework-20060204/ 转移到您希望保存 Java 项目的地方。例如，在我的系统上，我将 shale-framework-20060204/ 移动到 /usr/local/java 目录中。接下来，将 lib/ 目录移动到 Shale 目录中，所以最后的目录结构与 shale-framework-20060204/lib/ 类似。<br /><a class="f1"> </a></p><div class="guanggao"><span id="contentAdv"></span></div><p></p><p>　　<strong>2. 添加 Shale 库到 Web 应用程序中</strong></p><p>　　下一步是将所有 Shale JAR 文件和库添加到 Web 应用程序可以访问和使用它们的位置。步骤如下:</p><p>　　1. 如果在 servlet 引擎中没有包含 JSF，那么将 shale-framework-20060204/lib/jsf-ri/jsf-api.jar 和 shale-framework-20060204/lib/jsf-ri/jsf-impl.jar 复制到应用程序的 WEB-INF/lib 目录中。</p><p>　　2. 将 shale-core.jar、shale-clay.jar、shale-tiles.jar 和 tiles-core.jar 从 shale-framework-20060204/dist/ 目录复制到 Web 应用程序的 WEB-INF/lib 目录。</p><p>　　3. 将以下 Shale 依赖项复制到 Web 应用程序的 WEB-INF/lib 目录:</p><ul><li>　　o shale-framework-20060204/lib/commons-beanutils/commons-beanutils.jar 
</li><li>　　o shale-framework-20060204/lib/commons-chain/commons-chain.jar 
</li><li>　　o shale-framework-20060204/lib/commons-digester/commons-digester.jar 
</li><li>　　o shale-framework-20060204/lib/commons-logging/commons-logging.jar 
</li><li>　　o shale-framework-20060204/lib/commons-validator/commons-validator.jar</li></ul><p>　　4. 如果要使用 Shale 的 Spring 集成特性，那么将 shale-spring.jar 从 shale-framework-20060204/dist/ 复制到 Web 应用程序的 WEB-INF/ 目录。要完成这个步骤，还必须确保 Spring 的打包 JAR 文件也在 Web 应用程序的 WEB-INF/lib 目录中。这个 JAR 文件名为 spring.jar，如果您还没有这个文件的话，可以在 shale-framework-20060204/lib/shaleframework/ 目录中找到它。</p><p>　　5. 如果正在使用 Java 5.0，那么将 shale-tiger.jar 从 shale-framework-20060204/dist/ 复制到 Web 应用程序 的 WEB-INF/lib 目录中。只有在使用 Java 5.0 的时候才需要执行这一步;否则，servlet 引擎和使用 Shale 的 Web 应用程序就会出问题。</p><p>　　再往后走就开始复杂起来(是的，这些复制操作是较容易的一部分)。接下来的事情未必都要用最难的方式去做，至少我应该让您有机会选择 试试容易的方法。使 Shale 在系统上运行这个任务的确存在捷径;您已经知道手动设置 Shale 的过程比较复杂，接下来有必要看看 “简便” 方法。</p><p>　　<strong>更容易的方法</strong></p><p>　　重新访问 Shale 下载站点，并下载名称类似于 shale-starter-20060204.zip 的 “starter” 应用程序。解压这个归档文件，将得到一个名为 shale-starter/ 的目录。这是一个基本上配置好的 Shale Web 应用程序，用于帮助避免前一节详细介绍的复制和配置工作。首先要做的是将 shale-starter/ 目录重新命名成应用程序以后要使用的名称，例如可以将它命名为 first-shale/。进入 first-shale/ 目录，在这里可以看到一些文件和子目录。</p><p>　　在 first-shale/ 目录中，创建一个名为 build.properties 的新文件。通过这个文件可以定制如何构建 Shale starter 应用程序，并确保该应用程序适合您的环境设置。清单 1 展示了一个基本的 build.properties 文件，可以根据自己的环境对其进行定制。</p><p>　　清单 1. Shale starter 应用程序的示例 build.properties</p><p></p><table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0"><tbody><tr><td style="WORD-WRAP: break-word" bgcolor="#f3f3f3"><p><font face="Verdana" color="#000000"># Basic project information<br />project.copyright=My project, Copyright ? 2006<br />project.name=My First Shale Application<br />project.vendor=IBM DeveloperWorks<br />project.vendor.id=com.ibm.dw</font></p><p><font face="Verdana" color="#000000"># Java package and context path for servlet engine<br />project.package=com.ibm.dw.firstShale<br />project.path=first-shale</font></p><p><font face="Verdana" color="#000000"># Directory for Shale distribution - change this for your system<br />shale.dir=/usr/local/java/shale-framework-20060204</font></p><p><font face="Verdana" color="#000000"># Directory for all your libraries - change this for your system<br />lib.dir=/usr/local/java/shale-framework-20060204/lib</font></p></td></tr></tbody></table><p>　　根据系统设置好这些属性后，便可以运行 ant。Shale starter 应用程序开始构建 并(假设已经正确地设置了路径)创建一个示例应用程序。如果有问题，则构建脚本输出错误消息;这些错误消息都描述得很清楚，所以您应该可以更正任何错误。</p><p>　　构建过程的最后将生成一个名为 target/ 的新目录。进入这个目录，可以看到一个名为 first-app(即您在 build.properties 中为项目指定的名称)的子目录。大多数 servlet 引擎都允许将这个目录整个地复制到 servlet 引擎的 webapps/ 目录。例如，我使用的是 Tomcat，于是我将构建脚本创建的整个 first-shale 目录复制到 /usr/local/java/tomcat/webapps。<br /><a class="f1"> </a></p><div class="guanggao"><span id="contentAdv"></span></div><p></p><p>　　<strong>构建 WAR 文件</strong></p><p>　　如果使用的 servlet 引擎要求提供 WAR 文件，那么可以使用相同的 Shale starter 应用程序的构建文件，只需略微修改一下。由于还没有为这个 Shale 应用程序编写任何 Java 文件，当您请求一个 WAR 文件时，构建脚本将出现错误(在 build.xml 中有查找文件的 JavaScript 命令，但是没有找到任何文件)。为了修复这个问题，打开 build.xml 文件，找到以 “javadoc” 开头且如下所示的代码:</p><p></p><table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0"><tbody><tr><td style="WORD-WRAP: break-word" bgcolor="#f3f3f3"><p><font face="Verdana" color="#000000">&lt;!-- ===================== Generate Documentation ======================== --&gt;</font><font face="Verdana" color="#000000"><br />  &lt;target         name="javadocs" depends="compile"<br />           description="Create JavaDocs"&gt;</font></p><p>    &lt;mkdir         dir="${build.docs.dir}"/&gt;</p><p>    &lt;javadoc<br />            sourcepath="${src.java.dir}"<br />               destdir="${build.docs.dir}"<br />                author="false"<br />               private="true"<br />               version="true"<br />                source="${project.source}"<br />          packagenames="${project.package}.*"<br />           windowtitle="${project.name} (Version ${project.version})"<br />              doctitle="${project.name} (Version ${project.version})"<br />                bottom="${project.copyright}"&gt;<br />      &lt;classpath refid="compile.classpath"/&gt;<br />    &lt;/javadoc&gt;</p><p>    &lt;copy        todir="${build.docs.dir}"&gt;<br />      &lt;fileset     dir="${src.java.dir}"<br />              includes="**/*.gif"/&gt;<br />    &lt;/copy&gt;</p><p>  &lt;/target&gt;</p></td></tr></tbody></table><p>　　现在，注释掉 javadoc 任务，如下所示:</p><p></p><table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0"><tbody><tr><td style="WORD-WRAP: break-word" bgcolor="#f3f3f3"><p><font face="Verdana" color="#000000">  &lt;!-- ===================== Generate Documentation ======================== --&gt;</font><font face="Verdana" color="#000000"><br />  &lt;target         name="javadocs" depends="compile"<br />           description="Create JavaDocs"&gt;</font></p><p>    &lt;mkdir         dir="${build.docs.dir}"/&gt;<br />&lt;<br />    &lt;javadoc<br />            sourcepath="${src.java.dir}"<br />               destdir="${build.docs.dir}"<br />                author="false"<br />               private="true"<br />               version="true"<br />                source="${project.source}"<br />          packagenames="${project.package}.*"<br />           windowtitle="${project.name} (Version ${project.version})"<br />              doctitle="${project.name} (Version ${project.version})"<br />                bottom="${project.copyright}"&gt;<br />      &lt;classpath refid="compile.classpath"/&gt;<br />    &lt;/javadoc&gt;<br />--&gt;<br />    &lt;copy        todir="${build.docs.dir}"&gt;<br />      &lt;fileset     dir="${src.java.dir}"<br />              includes="**/*.gif"/&gt;<br />    &lt;/copy&gt;</p><p>  &lt;/target&gt;</p></td></tr></tbody></table><p>　　一旦开始为 Shale 应用程序开发 Java 代码，便不必这样做。不过对于现在，这样做可以解决上述问题。保存修改后的 build.xml 并运行 ant dist。Ant 编译和装配 starter 应用程序，并在 dist/ 目录中创建一个新的 WAR 文件。例如，我运行 ant dist 后得到一个 dist/first-shale-0.1.war 文件。现在可以将这个 WAR 文件复制到 servlet 引擎的 webapps/ 目录。</p><a class="f1"><div class="guanggao"><span id="contentAdv"></span></div><p></p><p>　　<strong>测试安装情况</strong></p><p>　　如果完成了以上步骤，不管选择的安装路径是什么，都应该可以启动 servlet 引擎并通过地址 http://your.host.name/first-shale 访问 Shale 应用程序。例如，如果在本地机器上运行 Tomcat，那么最终可以访问的地址是 http://localhost:8080/first-shale。如果一切正常，那么应该可以看到如图 2 所示的简单页面:</p><p><a href="http://searchwebservices.techtarget.com.cn/TLimages/picview/?/imagelist/06/19/2566wn3939rl.gif" target="_blank"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" alt="点击放大此图片" src="http://searchwebservices.techtarget.com.cn/imagelist/06/19/2566wn3939rl_s.gif" border="1" /></a></p><p>　　图 2. Shale starter 应用程序证明一切没问题</p><p>　　看起来似乎做了这么多工作却所得甚少，但是要考虑到，通过打开并编辑一个简单的 build.properties 文件，可以避免大量繁杂的复制和配置工作。您将发现，从空白的 Shale starter 应用程序开始总是开发新的 Shale 应用程序最容易的方式。实际上，当在下一篇文章中开始开发 Shale 应用程序的时候，将使用空白的 starter 应用程序作为开始的基础。</p><p>　　<strong>Shale 用例</strong></p><p>　　关于 Shale 的下载和安装就介绍到这里，不过我们还是再花点儿时间从 Shale 主下载站点下载 Shale 的用例 WAR 应用程序。找到一个文件名形如 shale-usecases-20060204.war 的文件。下载该文件，并将它放入 servlet 引擎的 webapps/ 目录，然后进入到这个 WAR。在我的系统上，访问 http://localhost:8080/shale-usecases-20060204/ 并得到如图 3 所示的屏幕:</p><p><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="http://searchwebservices.techtarget.com.cn/imagelist/06/19/xk614kk2k1qr.gif" border="1" /></p><p>　　图 3. Shale 用例应用程序</p><p>　　您应该花些时间来看看这个用例应用程序。它有关于 Shale 中 Validator 和远程报告等特性的很好的演示，并有一个简单的 Ajax 应用程序。通过浏览这些用例，您可以了解到即使是简单的 Shale 应用程序也可以做许多事情。</p><p>　　不过这里要提一个忠告:有些用例仍在开发中，取决于您何时下载每晚构建，可能发现有些用例不能正常工作。不过总是可以晚些时候再下载这些用例应用程序，看看有些问题是否已经被修复。虽然存在这些小问题，但是用例应用程序仍然是取得对 Shale 的基本印象的一种好途径。</p><a class="f1"><div class="guanggao"><span id="contentAdv"></span></div><p></p><p><font face="Verdana" color="#000000">       <strong>深入研究 Shale！</strong></font></p><p>　　大多数 Web 开发人员向来只是使用已有的框架(例如 Shale、Struts 或 Spring)来开发他们的 Web 应用程序，而没有做别的事情。当然这没有什么错，但是如果想理解一种框架以及它所涉及的技术，那么只能对框架本身做深入的研究。</p><p></p><p>　　对于 Shale(当然也包括 Struts)，通过查看框架的内部，您可以学到大量关于 servlet　和 Web 开发的知识。如果想在自己的项目中使用一些 Shale 依赖项，这样做还可以获得难以置信的帮助。如果您对通过 Java 应用程序进行日志管理感兴趣，那么通过 Shale 来熟悉 Apache Logging 项目比阅读任何文章都要有效得多。对于 Jakarta Commons BeanUtils、Chain 或 Digester 项目也是一样。这些都是很好的工具，对于开发人员很有用，所以花几个星期或几个月的时间探索一下 Shale 对于这些领域是一个很好的学习经历。</p><p>　　由于本文是对 Shale 进行深入探讨的系列中的第一期，因此如果我不对几个对于 Shale 项目入门来说至关重要的方面进行讨论的话，就是不负责任了。</p><p>　　<strong>亲密接触源代码</strong></p><p>　　不幸的是，关于 Shale 中涉及的开发过程的文档并不多，所以如果您想直接使用 Shale 源代码的话，需要用点儿技巧。一般来说，我这里给出的关于下载 Shale 并将它作为框架使用的说明也适用于下载 Shale 的源代码。每晚构建包含 Shale 的所有源代码，并且代码的每个目录中都有一个 build.xml 文件。</p><p>　　需要将下载的 Shale 的根目录下的 build.properties.sample 文件复制到一个名为 build.properties 的文件中(去掉原始文件名尾部的 “.sample”)。清单 2 展示了这个文件的一个示例，为了简洁起见，这里省略了其中一些注释:</p><p>　　清单 2. 示例 Shale 构建文件</p><p></p><table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0"><tbody><tr><td style="WORD-WRAP: break-word" bgcolor="#f3f3f3"><p><font face="Verdana" color="#000000"># This file contains example property settings that you would use to customize<br /># your build environment to build the Struts Shale Library from<br /># source code.  To use this file, make a copy of it in "build.properties" and<br /># customize the values as required.</font></p><p><font face="Verdana" color="#000000"># Root directory into which you have unpacked the Shale Framework release.<br />root.dir=${basedir}</font></p><p><font face="Verdana" color="#000000"># Fully qualified pathname of the directory into which you have unpacked<br /># a binary distribution of the JavaServer Faces Reference Implementation<br />jsfri.dir=/usr/local/jsf-1_1_01</font></p><p><font face="Verdana" color="#000000">findbugs.outputFile=${root.dir}/find-bugs.html<br />lib.dir=${root.dir}/lib<br />jsf.home = ${lib.dir}/myfaces<br />jsf-api.jar = ${jsf.home}/myfaces-api.jar<br />jsf-impl.jar = ${jsf.home}/myfaces-impl.jar</font></p><p><font face="Verdana" color="#000000"># The absolute or relative pathname of the Apache Struts <br /># distribution<br />struts.home = /usr/local/jakarta-struts</font></p><p><font face="Verdana" color="#000000">spring.home=${lib.dir}/springframework<br />findbugs.home = /usr/local/findbugs-0.8.6</font></p></td></tr></tbody></table><p>　　为了与您的系统相匹配，需要更改这个构建文件中大部分的路径。默认情况下，${basedir} 指向运行 Ant 时所在的目录，因此如果是从下载的 Shale 的根目录下运行 Ant，那么就刚好不用改路径了。但是对于其他路径，应该改为适当的与系统相匹配的路径。例如，如果您的 JSF 参考实现在 c:/java/jsf-1_1_02 中，那么使用 jsfri.dir 目录所在的路径。大多数默认路径都适合于使用 MyFaces(请参阅 “MyFaces 还是 JavaServer Faces”)，但是当然也可以使用 Sun 的 JSF 实现，并对这些路径作相应的更改。另外还需要设置 Struts、Spring(这是可选的，对于核心 Shale 框架来说不必要)和 FindBugs 项目的路径。</p><p>　　<strong>Ant 登场</strong></p><p>　　设置好这些文件的路径后，就可以在 Shale 的根目录中运行 Ant。但是，首先应该运行 ant download-dependencies。您当然也已经注意到，Shale 有很多 依赖项，而通过使用 Ant 自动下载这些依赖项可以为您节省很多时间，也令您轻松不少。Ant 脚本还负责设置路径，以便使 Shale 与那些依赖项连接起来。还应该运行 ant copy-jsf-ri 来处理一些特定于 JSF 的任务(具体细节不必关心，因为 Ant 会为您打点一切)。</p><p>　　在构建主 Shale 发行版之前，应该运行 ant clean 删除之前已有的构建后的代码。虽然这意味着整个构建时间会更长，但是可以确保所有代码将一致地构建。最后，运行 ant release，以便从头开始构建 Shale。当这个 Ant 脚本运行完成后(这要花一点儿时间)，就可以得到一个完整的、从源代码构建的 Shale 发行版。</p><p>　　<strong>关于邮件列表的只言片语</strong></p><p>　　开发源码项目几乎完全是通过电子邮件(再加上 Apache bug 跟踪数据库，在 参考资料 小节中有这方面的内容)来运作的。Shale 在这方面也是一样的，不过它仍然使用 Struts 的邮件列表。如果在使用 Shale 时有什么疑问，可以发送电子邮件到 user@struts.apache.org。但是当您开始开发真正的 Shale 内部组件时，应该将电子邮件发送到 dev@struts.apache.org。不管将电子邮件发送到哪里，都应该以 “[shale]” 开头，这样别人一下子就明白您是要问关于 Shale 的问题，而不是关于 Struts 的问题。预期在几个月后，当 Shale 开始成为独立的项目时，它也会有它自己的邮件列表。</p><p>　　这里稍微提醒一下，尤其是在发送电子邮件到开发列表的时候:做好自己的工作，问题要有的放矢。那些飘忽不定、模棱两可或缺乏思想的邮件很可能不会收到回复。如果您到处发送 “我想学习 Shale，请给我发送一些例子应用程序” 之类的邮件，甚至还可能得到粗鲁的回答。虽然这种提醒看上去有些傻，但事实就是这样。开发列表中总是充斥着这一类的问题，这些问题都是不受欢迎 的。通常，花点儿时间认真地斟酌您的问题，解释一下您使用的平台和软件的版本，并说明您已经试过了一些常用的步骤。这样一来，您的请求才会得到尊重并受到欢迎，也就更容易得到答案。开发人员的列表并不是令人生畏的，但最起码这样做显得您尊重别人。</p><p>　　<strong>结束语</strong></p><p>　　这个关于 Shale 的系列中的第一期文章说明，Shale 并不适合每一个人。Shale 没有提供一个打包好的、有编制好的文档并经过良好测试的产品，也没有附带自动安装程序和优雅的管理界面，这些都是很多 Web 开发人员期待 Tapestry 时代能提供的东西。虽然在以后版本的框架中会体现这些东西(除了完全打包)，但目前 Shale(从 2006 年初起)仍在开发过程中，并且 Shale 站点也基本上将它称为处于 “alpha” 状态的项目。Shale 中使用的很多组件是稳定和成熟的，但 Shale 本身仍然很年轻。如果您不能接受一些麻烦和困惑，那么可能会想过一年左右再开始使用它。</p><p>　　另一方面，如果您是一名对 Web 开发的前沿技术感兴趣的 Java 开发人员，那么真应该看看 Shale 项目。虽然安装 Shale 并使之工作要花费更多的精力，但是它完全有条件成为特别流行的 Web 开发框架。Shale 继承了 Struts，同时也提供了一些全新的东西，这本身就值得作一番调查。对于有兴趣成为开放源码项目中的一员的开发人员，Shale 也是值得投入精力的一个项目。</p></a>归于此类，因为shale与jsf有千丝万缕的联系<a class="f1"></a></a><img src ="http://www.blogjava.net/kingwell/aggbug/48623.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingwell/" target="_blank">KingWell</a> 2006-05-28 20:54 <a href="http://www.blogjava.net/kingwell/articles/48623.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web应用程序中的可重用性</title><link>http://www.blogjava.net/kingwell/articles/45457.html</link><dc:creator>KingWell</dc:creator><author>KingWell</author><pubDate>Wed, 10 May 2006 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/kingwell/articles/45457.html</guid><wfw:comment>http://www.blogjava.net/kingwell/comments/45457.html</wfw:comment><comments>http://www.blogjava.net/kingwell/articles/45457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingwell/comments/commentRss/45457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingwell/services/trackbacks/45457.html</trackback:ping><description><![CDATA[
		<span class="italicbodycopy">作者：Andrei Cioroianu<br />转自：Oracle Technology Network <a href="http://www.oracle.com/technology/global/cn/pub/articles/andrei_reuse.html">http://www.oracle.com/technology/global/cn/pub/articles/andrei_reuse.html</a><br /><br /></span>
		<p>
				<span class="boldbodycopy">了解如何利用 JSP 标记文件、JSF 和 Oracle ADF Faces 重用 Web 内容和 Java 代码。</span>
		</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="1" width="42%" bgcolor="#cccccc" border="0">
				<tbody>
						<tr>
								<td>
										<table cellspacing="0" cellpadding="2" width="100%" bgcolor="#ffffff" border="0">
												<tbody>
														<tr>
																<td>
																		<span class="boldbodycopy">本文相关下载：</span>
																		<br />
																		<img height="5" src="http://oracleimg.com/admin/images/ocom/bullet_5x5.gif" width="5" align="bottom" border="0" />
																		<a class="bodylink" href="http://www.oracle.com/technology/pub/files/webreuse_src.zip">示例代码</a>
																		<br />
																		<img height="5" src="http://oracleimg.com/admin/images/ocom/bullet_5x5.gif" width="5" align="bottom" border="0" />
																		<a class="bodylink" href="http://www.oracle.com/technology/global/cn/software/products/jdev/index.html">Oracle JDeveloper 10<em>g</em> (10.1.3)</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<span class="legalese">2005 年 10 月发表</span>
		</p>
		<p>
				<span class="bodycopy">代码重用是提高开发人员生产效率和应用程序可维护性的一种非常好的方式。您应当总是寻找设计良好的框架和可自定义的组件，而不是从头重来。应用程序特有的代码也可以在模块甚至相关项目间重用。后一种可重用性可使您快速修改，整体利用新特性，并减少测试和调试的时间。</span>
		</p>
		<p>
		</p>
		<span class="bodycopy">虽然这些听起来像是针对程序员的不错建议，但 Web 开发人员也应当注意这些事情。许多 Web 开发人员已经在使用诸如 Java Server Faces (JSF)、Oracle ADF Faces 和 Apache MyFaces 之类的框架，这些框架提供了许多内置组件并支持创建其他可重用组件。然而，很多时候是将许多 HTML 和 JSP 标记从一个 Web 页面复制粘贴到其他页面中，这意味着当 Web 内容改变时将不得不修改这些页面中的重复标记。此外，如果没有更新某些页面，那么应用程序的外观将会不一致。如果跨页面重用 UI 组件就不会发生这种情况了，这是因为发生变化时只需在一个地方进行编辑就可以了。</span>
		<p>
		</p>
		<span class="bodycopy">在本文中，我将提供一些在基于 JSF 和 ADF Faces 的 Web 应用程序中重用 UI 组件的最佳实践。您将了解到如何创建定义 Web 页面布局的模板，以及如何重用表单、菜单和按钮栏。您还将了解到如何转换现有的 JSP 页面以使它们更易于维护，以及如何将由 JSF 和 Oracle ADF Faces 提供的组件与 JSTL 和现代 JSP 特性（例如标记文件）一起使用。</span>
		<p>
		</p>
		<span class="parahead1">Java Web 可重用特性</span>
		<p>
		</p>
		<span class="bodycopy">自从第一个版本起，JSP 就已经提供了一些鼓励可重用的基本机制，例如 JavaBeans 支持、基于 Servlets API RequestDispatcher 的 <tt>&lt;%@include%&gt;</tt> 指令和 &lt;jsp:include&gt; 标记。JSTL 增加了 &lt;c:import&gt; 标记，它使您能够包含某个资源的内容，该资源可以位于同一个应用程序、服务器中，甚至也可以在远程服务器上。Struts Tiles 围绕着这种内容包含特性构建了一个完整的框架。JSF 也支持这一特性，允许您构建使用 <tt>&lt;f:subview&gt;</tt> 标记的子表单。JSP 2.0 增加了一个称为“隐式包含”的新特性。这些特性使用 <tt>&lt;include-prelude&gt;</tt> 和 <tt>&lt;include-coda&gt;</tt> 在 web.xml 文件中声明。正如您所能看到的，虽然页面/片断包含种类各异，但每一种都有其自己的用途和上下文。 </span>
		<p>
		</p>
		<span class="bodycopy">对自定义标记的支持从 JSP 1.1 就有了，它为构建标记库提供了一个 API。JSP 1.2 对该 API 进行了增强，但很多人认为它太复杂了。因此，JSP 2.0 定义了一个具有相同功能的全新 API。这个为标记库提供的新 API 称为简单标记 API，旧 API 现在称为标准标记 API。许多 Web 框架（如 Struts、JSF 和 JSTL）仍使用标准标记 API，以便可以与 JSP 1.2 以及 JSP 2.0 一起使用。简单标记 API 是另一种 JSP 2.0 特性 — 标记文件 — 的基础，该特性使您能够使用 JSP 语法构建标记库。除了简单标记和标记文件之外，JSP 2.0 规范还定义了 EL 函数，后者使您能够使用 EL 语法从 JSP 页面中调用静态 Java 方法。</span>
		<p>
		</p>
		<span class="bodycopy">JSF 标准将组件定义为它的可重用单元。这些组件比自定义标记更强大，但也更难设计和实施。因为有几个公司和开放源代码机构正在制作可供使用的 JSF 组件库，所以您可能不需要构建自己的 JSF 组件。本文的示例使用了 Oracle ADF Faces，它是基于 JSF 标准的最先进的框架。</span>
		<p>
		</p>
		<span class="bodycopy">
				<strong>创建页面模板。</strong>典型 Web 应用程序的所有页面共享一个公共布局，该布局可以定义在一个地方，如 JSP 标记文件中。该模板可以生成标题和正文标记、应用程序的菜单以及在所有页面中出现的其他部分。此外，它可以包含用于加载资源绑定、设置 JSP 变量等的设置标记。在应用程序的每个 Web 页面中重复该标记是没有意义的。在这一部分中，您将了解如何使用 Oracle JDeveloper 10<em>g</em> (10.1.3)（撰写此文时为早期试用版）基于 JSF 和 Oracle ADF Faces 构建自定义模板。 </span>
		<p>
		</p>
		<span class="bodycopy">JDeveloper 提供了一个创建 JSF 页面模板的向导。从 File 菜单中选择 New 项，打开 New Gallery 窗口。然后，转至 Web Tier 中的 JSF 类别，在右侧面板中选择 JSF JSP Template 并单击 OK：</span>
		<p>
		</p>
		<center>
				<img alt="图 1" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f1.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">单击 Next 跳过 Welcome 页面，然后选择您使用的 J2EE 版本，并再次单击 Next：</span>
		<p>
		</p>
		<center>
				<img alt="图 2" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f2.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">为模板提供一个文件名：</span>
		<p>
		</p>
		<center>
				<img alt="图 3" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f3.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">选择组件绑定样式：</span>
		<p>
		</p>
		<center>
				<img alt="图 4" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f4.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">指定是否要使用错误页面：</span>
		<p>
		</p>
		<center>
				<img alt="图 5" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f5.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">选择要使用的标记库：</span>
		<p>
		</p>
		<center>
				<img alt="图 6" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f6.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">提供页面标题和其他页面属性：</span>
		<p>
		</p>
		<center>
				<img alt="图 7" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f7.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">单击 Finish。JDeveloper 将创建该模板并在可视化编辑器中将其打开。您可以使用 Component Palette 将 JSF 和 Oracle ADF Faces 组件添加到该模板中。然后，您可以在 New Gallery 窗口中从 Template 中选择 JSF JSP，基于您刚创建的模板创建 JSF 页面。这是从模板构建页面的一种非常简单的方法。另一种方法是将该共用的 JSF 标记移到一个可重用的标记文件中。以下段落使用了第二种方法。 </span>
		<p>
		</p>
		<span class="bodycopy">
				<strong>创建标记文件。</strong>从 File 菜单中选择 New 项，打开 New Gallery 窗口。然后，转至 Web Tier 中的 JSP 类别，在右侧面板中选择 JSP Tag File 并单击 OK： </span>
		<p>
		</p>
		<center>
				<img alt="图 8" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f8.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">JDeveloper 将打开一个创建 JSP 标记文件的向导窗口。单击 Next 跳过 Welcome 页面，在 File Name 域中输入 pageTemplate.tag 并单击 Next：</span>
		<p>
		</p>
		<center>
				<img alt="图 9" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f9.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">现在您就可以定义模板标记的属性了。假定您正在构建一个基于 Web 的向导，您希望每个页面都有一个步骤 ID 和一个标题。标记文件需要该信息来为每个页面自定义模板标记。单击 Add 按钮，输入 step 属性名，并将 Required 设为 true。对另一个名称为 title 的属性执行同样的操作：</span>
		<p>
		</p>
		<center>
				<img alt="图 10" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f10.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">单击 Next 和 Finish。JDeveloper 将在 WEB-INF 目录的 tags 子目录下创建 pageTemplate.tag 文件。用 &lt;%@attribute%&gt; 指令定义这两个标记属性： </span>
		<pre>&lt;%@ attribute name="step" required="true" %&gt;
&lt;%@ attribute name="title" required="true" %&gt;
</pre>
		<span class="bodycopy">在创建标记文件之后，JDeveloper 将打开它进行编辑。以下段落介绍了本文通篇用到的示例应用程序的模板代码。</span>
		<p>
		</p>
		<span class="bodycopy">
				<strong>在标记文件中使用 JSF 和 Oracle ADF Faces。</strong>无论您是否想在普通页面的标记文件内使用这些框架，您都必须用 <tt>&lt;%@taglib%&gt;</tt> 指令来对其进行声明。在 Component Palette 中选择 JSP，然后单击 Taglib。您需要在一个对话框中输入要使用的标记库的 URI 和前缀： </span>
		<p>
		</p>
		<center>
				<img alt="图 11" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f11.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">您还可以使用 Component Palette 来将任何标记拖放到 JSP 页面上，如果它的 <tt>&lt;%@taglib%&gt;</tt> 指令不在页面中，那么 JDeveloper 将自动添加它。pageTemplate.tag 示例使用了四个标记库：JSTL Core、JSF Core、Oracle ADF Faces Core 和 Oracle ADF Faces HTML：</span>
		<pre>&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %&gt;
&lt;%@ taglib prefix="af" 
uri="http://xmlns.oracle.com/adf/faces" %&gt;
&lt;%@ taglib prefix="afh" 
uri="http://xmlns.oracle.com/adf/faces/html" %&gt;
</pre>
		<span class="bodycopy">在同时使用 JSF 和 JSTL 时，或者在标记文件中使用 JSF 时有一件非常重要的事情您必须始终牢记：JSF 框架不支持页面范围，后者是 JSTL 标记的默认 JSP 范围。因此，您应当显式指定与 JSTL、JSF 和 Oracle ADF Faces 的标记结合使用的 JSP 变量的请求范围。此外，标记文件的所有属性都可以通过保存在页面范围内的 JSP 变量来访问。您必须复制请求范围内的属性，以便它们可以和 JSF 和 Oracle ADF Faces 标记一起使用：</span>
		<pre>&lt;c:set var="pageTitle" scope="request" value="${pageScope.title}"/&gt;
</pre>
		<span class="bodycopy">您还可以将属性的值存储在由 JSF 管理的 Bean 内。假定您有一个名称为 WizardBean 的类，该类在 faces-config.xml 中被配置为受管 Bean：</span>
		<pre>&lt;faces-config&gt;
    ...
&lt;managed-bean&gt;
&lt;managed-bean-name&gt;wizard&lt;/managed-bean-name&gt;
&lt;managed-bean-class&gt;webreuse.WizardBean&lt;/managed-bean-class&gt;
&lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;
&lt;/managed-bean&gt;
    ...
&lt;/faces-config&gt;
</pre>
		<span class="bodycopy">示例 Bean 有一个 currentStep 属性，您可以在该属性中存储 step 属性的值。该操作可以利用 JSTL 的 <tt>&lt;c:set&gt;</tt> 标记来完成，但您必须确保该 Bean 存在于会话范围中（如 faces-config.xml 文件中所声明的那样）。只有在 JSF EL 表达式中首次引用受管 Bean 实例时，JSF 才会创建该实例。如果在访问受管 Bean 的 JSF 或 Oracle ADF Faces 标记之前执行 JSTL 标记，那么，您应当使用 <tt>&lt;jsp:useBean&gt;</tt>以确保该 Bean 实例存在于会话范围中。如此，您就可以安全地使用 <tt>&lt;c:set&gt;</tt> 标记了：</span>
		<pre>&lt;jsp:useBean class="webreuse.WizardBean" 
id="wizard" scope="session"/&gt;
&lt;c:set target="${wizard}" property="currentStep" 
value="${pageScope.step}"/&gt;
</pre>
		<span class="bodycopy">以上代码可以手动输入，或者可以使用 JDeveloper 的 Component Palette。选择 JSP，然后单击 UseBean，添加 <tt>&lt;jsp:useBean&gt;</tt> 标记。JDeveloper 将打开一个对话框，您必须在其中提供该标记的属性： </span>
		<p>
		</p>
		<center>
				<img alt="图 12" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f12.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">最后，您可以在 pageTemplate.tag 文件中添加模板代码：</span>
		<pre>&lt;f:view&gt;
&lt;afh:html&gt;
&lt;afh:head title="#{pageTitle}"/&gt;
&lt;afh:body&gt;
&lt;af:panelPage title="#{pageTitle}"&gt;
&lt;jsp:doBody/&gt;
&lt;/af:panelPage&gt;
&lt;/afh:body&gt;
&lt;/afh:html&gt;
&lt;/f:view&gt;
</pre>
		<span class="bodycopy">所有的 JSF 和 Oracle ADF Faces 组件都必须置于 &lt;f:view&gt; 内部。<tt>&lt;afh:html&gt;</tt>、<tt>&lt;afh:head&gt;</tt> 和 <tt>&lt;afh:body&gt;</tt> 组件将生成具有相同名称的 HTML 标记。&lt;af:panelPage&gt; 组件显示页面标题。然后，<tt>&lt;jsp:doBody&gt;</tt> 将调用您放在使用模板标记的 JSP 页面中的 <tt>&lt;tags:pageTemplate&gt;</tt> 和 <tt>&lt;/tags:pageTemplate&gt;</tt> 之间的 JSP 内容。这种 JSP 页面将类似于：</span>
		<pre>&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;tags:pageTemplate step="..." title="..."&gt;
... JSP content executed by doBody ...
&lt;/tags:pageTemplate&gt;
</pre>
		<span class="bodycopy">当执行该页面时，标记文件将生成 <tt>&lt;html&gt;</tt>、<tt>&lt;head&gt;</tt> 和 <tt>&lt;body&gt;</tt> 标记以及页面标题和其他标题标记。然后，模板标记文件将调用包在 <tt>&lt;tags:pageTemplate&gt;</tt> 和 <tt>&lt;/tags:pageTemplate&gt;</tt> 之间的 JSP 内容。在此之后，标记文件可能会生成一个页脚，并用 <tt>&lt;/body&gt;</tt> 和 <tt>&lt;/html&gt;</tt> 来完成页面。下一部分将在基于 Web 的向导的页面中使用模板标记。 </span>
		<p>
		</p>
		<span class="parahead1">重用表单、菜单和其他 UI 组件</span>
		<p>
		</p>
		<span class="bodycopy">利用 JSF 和 Oracle ADF Faces 组件构建的 UI 面板可以使用 "subviews" 或 JSP 标记文件在多个页面中重用。前一种解决方案与旧的 JSP 1.2 版本兼容，但标记文件更灵活。这一部分将介绍如何基于 JSF 和 Oracle ADF Faces 将一个 Web 表单分成多个页面和标记文件。当用户第一次访问应用程序时，他将使用包含后退和前进按钮的向导式界面来逐步浏览这些页面。在此以后，他可能想更新最初提供的信息。在这种情况下，用户需要使用基于菜单的界面直接访问应用程序的页面。 </span>
		<p>
		</p>
		<span class="bodycopy">可以在上述的两种情况下使用同一种 Web 表单。此外，所有的表单可以组合在一个确认页面中，该页面可以让用户以只读模式查看信息。当用户必须修改其中一个表单时，可以编辑单个文件。每一个修改都将显示在向导风格的界面（用于从用户那获取信息）、确认页面（用于查看信息）和基于菜单的界面（由用户用于更新信息）中。如果不重用表单，您将必须在三个不同的地方进行相同的修改。</span>
		<p>
		</p>
		<span class="bodycopy">
				<strong>开发 Backing Bean。</strong>您在前一部分中已经发现，示例应用程序使用了一个名称为 WizardBean 的 Backing Bean。pageTemplate.tag 文件设置了 currentStep 属性，该属性保存用户在浏览器中看到的当前表单的步骤 ID。示例应用程序在确认页面（第四步）之前使用了三个表单，确认页面的 ID 由 <tt>MAX_STEP</tt> 常量来定义。将该常量公开为名为 maxStep 的 bean 属性，以便可以在 Web 页面中使用 JSF EL 来访问它：</span>
		<pre>package webreuse;

public class WizardBean implements java.io.Serializable {
public final static int MAX_STEP = 4;
private int currentStep;
private String connName, connType;
private String userName, password, role;
private String driver, hostName, sid;
private int jdbcPort;

public int getMaxStep() {
return MAX_STEP;
    }

public int getCurrentStep() {
return currentStep;
    }

public void setCurrentStep(int currentStep) {
this.currentStep = currentStep;
    }

    ...
}
</pre>
		<span class="bodycopy">除 <tt>currentStep</tt> 和 <tt>maxStep</tt> 之外，WizardBean 类还有几个其他的属性可用于保存用户提供的向导参数：<tt>connName</tt>、<tt>connType</tt>、<tt>userName</tt>、<tt>password</tt>、<tt>role</tt>、<tt>driver</tt>、<tt>hostName</tt>、<tt>sid</tt> 和 <tt>jdbcPort</tt>。该示例应用程序与用户数据无关，但在实际情况中，向导将用它来配置数据库连接。WizardBean 还实施了几个在用户单击向导的按钮时将执行 JSF 操作。这些方法稍后将在本部分中进行介绍。</span>
		<p>
		</p>
		<span class="bodycopy">要创建您自己的 Bean，您可以在 File 菜单中选择 New 来打开 New Gallery 窗口。然后，转至 General 中的 Simple Files 类别，在右侧面板中选择 Java Class，并单击 OK：</span>
		<p>
		</p>
		<center>
				<img alt="图 13" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f13.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">提供类名和程序包名称。然后单击 OK，创建该类：</span>
		<p>
		</p>
		<center>
				<img alt="图 14" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f14.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">在声明一个字段之后（例如 <tt>private int currentStep</tt>），右键单击其名称并选择 Generate Accessors。单击 OK，生成 get 和 set 方法：</span>
		<p>
		</p>
		<center>
				<img alt="图 15" src="http://www.oracle.com/technology/pub/images/andrei_reuse_f15.jpg" />
		</center>
		<p>
		</p>
		<span class="bodycopy">
				<strong>创建可重用表单。</strong>将向导的主表单编写为可重用标记文件。第一个表单 (form1.tag) 包含一个文本域和一个下拉列表：</span>
		<pre>&lt;!-- form1.tag --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:inputText id="connName" required="true" columns="40"
label="Connection Name:" value="#{wizard.connName}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;

&lt;af:selectOneChoice id="connType" required="true"
label="Connection Type:" value="#{wizard.connType}"
readOnly="#{wizard.currentStep == wizard.maxStep}"&gt;
&lt;af:selectItem label="Oracle (JDBC)" value="Oracle_JDBC"/&gt;
&lt;/af:selectOneChoice&gt;
</pre>
		<span class="bodycopy">第二个表单 (form2.tag) 包含三个文本域：</span>
		<pre>&lt;!-- form2.tag --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:inputText id="userName" required="true" columns="40"
label="User Name:" value="#{wizard.userName}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;

&lt;af:inputText id="password" required="true" columns="40"
label="Password:" value="#{wizard.password}" secret="true"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;

&lt;af:inputText id="role" required="false" columns="40"
label="Role:" value="#{wizard.role}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;
</pre>
		<span class="bodycopy">第三个表单 (form3.tag) 与其他两个表单类似：</span>
		<pre>&lt;!-- form3.tag --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:selectOneChoice id="driver" required="true"
label="Driver:" value="#{wizard.driver}"
readOnly="#{wizard.currentStep == wizard.maxStep}"&gt;
&lt;af:selectItem label="thin" value="thin"/&gt;
&lt;af:selectItem label="oci8" value="oci8"/&gt;
&lt;/af:selectOneChoice&gt;

&lt;af:inputText id="hostName" required="true" columns="40"
label="Host Name:" value="#{wizard.hostName}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;

&lt;af:inputText id="sid" required="true" columns="40"
label="SID:" value="#{wizard.sid}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;

&lt;af:inputText id="jdbcPort" required="true" columns="40"
label="JDBC Port:" value="#{wizard.jdbcPort}"
readOnly="#{wizard.currentStep == wizard.maxStep}"/&gt;
</pre>
		<span class="bodycopy">正如您可能已经注意到的那样，三个标记文件中没有一个包含了用于排列 Oracle ADF Faces 组件的标记。不含任何布局标记使得您可以独立地使用表单标记，或在确认页面中组合它们。Oracle ADF Faces 提供了一个名称为 &lt;af:panelForm&gt; 的强大组件，它将自动执行布局。除了主要的组件之外，表单通常包含有其他的标记，例如 <tt>&lt;h:messages globalOnly="true"/&gt;</tt> 和 <tt>&lt;af:objectLegend name="required"/&gt;</tt>。所有这些标记都可以集中在一个名为 formTemplate.tag 的标记文件中：</span>
		<pre>&lt;!-- formTemplate.tag --&gt;

&lt;%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %&gt;
&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;h:panelGrid columns="1" border="0" cellspacing="5"&gt;
&lt;h:messages globalOnly="true"/&gt;
&lt;af:objectLegend name="required"/&gt;
&lt;af:panelForm&gt;
&lt;jsp:doBody/&gt;
&lt;/af:panelForm&gt;
&lt;/h:panelGrid&gt;
</pre>
		<span class="bodycopy">使用 pageTemplate.tag 和 formTemplate.tag 的 JSF 页面将类似于：</span>
		<pre>&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;
&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;tags:pageTemplate step="..." title="..."&gt;
	&lt;af:form id="..."&gt;
		...
		&lt;tags:formTemplate&gt;
			&lt;tags:form123/&gt;
		&lt;/tags:formTemplate&gt;
		...
	&lt;/af:form&gt;
&lt;/tags:pageTemplate&gt;
</pre>
		<span class="bodycopy">在前面的代码段中，<tt>&lt;tags:form123/&gt;</tt> 代表向导的三个主表单中的任意一个（&amp;<tt>lt;tags:form1/&gt;</tt>、<tt>&lt;tags:form2/&gt;</tt> 或 <tt>&lt;tags:form3/&gt;</tt>）。除了这些表单的组件之外，<tt>&lt;af:form&gt;</tt> 可能包含其他的 JSF 和 Oracle ADF Faces 组件（例如按钮）。下一段介绍了使用 <tt>&lt;tags:pageTemplate&gt;</tt>、<tt>&lt;tags:formTemplate&gt;</tt>、<tt>&lt;tags:form1&gt;</tt>、<tt>&lt;tags:form2&gt;</tt> 和 <tt>&lt;tags:form3&gt;</tt> 的应用程序页面。这些具体的例子充分说明了利用可重用标记文件构建 JSF 用户界面的实际好处。 </span>
		<p>
		</p>
		<span class="bodycopy">
				<strong>向导风格的界面。</strong>基于 Web 的向导的页面将包含标记为 Back、Next 和 Finish 的按钮。可以在一个名为 stepButtons.tag 的标记文件中定义这些按钮：</span>
		<pre>&lt;!-- stepButtons.tag --&gt;

&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:panelButtonBar&gt;

&lt;af:singleStepButtonBar
selectedStep="#{wizard.currentStep}"
maxStep="#{wizard.maxStep}"
previousAction="#{wizard.previousAction}"
nextAction="#{wizard.nextAction}"/&gt;

&lt;c:if test="${wizard.currentStep == wizard.maxStep}"&gt;
&lt;af:commandButton text="Finish"
action="#{wizard.finishAction}"/&gt;
&lt;/c:if&gt;

&lt;/af:panelButtonBar&gt;
</pre>
		<span class="bodycopy">WizardBean 类包含当用户单击按钮时将执行的操作方法：</span>
		<pre>package webreuse;

public class WizardBean implements java.io.Serializable {
    ...

public String previousAction() {
if (currentStep &lt;= 1)
return null;
else {
currentStep--;
return "step" + currentStep;
        }
    }

public String nextAction() {
if (currentStep &gt;= getMaxStep())
return null;
else {
currentStep++;
return "step" + currentStep;
        }
    }

public String finishAction() {
currentStep = 0;
return "finished";
    }

    ...
}
</pre>
		<span class="bodycopy">操作方法返回的结果将在 faces-config.xml 文件的导航规则中使用：</span>
		<pre>&lt;faces-config&gt;
    ...
&lt;navigation-rule&gt;
&lt;from-view-id&gt;*&lt;/from-view-id&gt;
&lt;navigation-case&gt;
&lt;from-outcome&gt;step1&lt;/from-outcome&gt;
&lt;to-view-id&gt;/step1.jsp&lt;/to-view-id&gt;
&lt;/navigation-case&gt;
&lt;/navigation-rule&gt;
    ...
&lt;navigation-rule&gt;
&lt;from-view-id&gt;*&lt;/from-view-id&gt;
&lt;navigation-case&gt;
&lt;from-outcome&gt;step4&lt;/from-outcome&gt;
&lt;to-view-id&gt;/confirm.jsp&lt;/to-view-id&gt;
&lt;/navigation-case&gt;
&lt;/navigation-rule&gt;

&lt;navigation-rule&gt;
&lt;from-view-id&gt;*&lt;/from-view-id&gt;
&lt;navigation-case&gt;
&lt;from-outcome&gt;finished&lt;/from-outcome&gt;
&lt;to-view-id&gt;/index.jsp&lt;/to-view-id&gt;
&lt;/navigation-case&gt;
&lt;/navigation-rule&gt;
    ...
&lt;/faces-config&gt;
</pre>
		<span class="bodycopy">除了所有可见的组件之外，向导页面还将包含一个与 WizardBean 的 currentStep 属性绑定的隐藏字段。您已经看到了 pageTemplate.tag 将在每一次执行页面时设置该属性。然而，用户可能单击浏览器的后退按钮。作为该操作的结果，在浏览器中看到的当前步骤将与 currentStep 属性的值不符，因为浏览器将从其缓存中检索到页面，而不是请求执行 JSF 页面。</span>
		<p>
		</p>
		<span class="bodycopy">如果每一次用户单击按钮时 currentStep 值都与表单数据一起提交，则不会导致任何问题。hiddenData.tag 文件将 <tt>currentStep</tt> 作为一个隐藏字段包含在内。此外，如果 <tt>currentStep</tt> 等于 <tt>maxStep</tt>（这意味着标记在确认页面中使用），那么该标记文件将为所有 Bean 属性生成隐藏字段：</span>
		<pre>&lt;!-- hiddenData.tag --&gt;

&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %&gt;

&lt;h:inputHidden id="currentStep" value="#{wizard.currentStep}"/&gt;

&lt;c:if test="${wizard.currentStep == wizard.maxStep}"&gt;
&lt;h:inputHidden id="h_connName" value="#{wizard.connName}"/&gt;
&lt;h:inputHidden id="h_connType" value="#{wizard.connType}"/&gt;
&lt;h:inputHidden id="h_userName" value="#{wizard.userName}"/&gt;
&lt;h:inputHidden id="h_password" value="#{wizard.password}"/&gt;
&lt;h:inputHidden id="h_role" value="#{wizard.role}"/&gt;
&lt;h:inputHidden id="h_driver" value="#{wizard.driver}"/&gt;
&lt;h:inputHidden id="h_hostName" value="#{wizard.hostName}"/&gt;
&lt;h:inputHidden id="h_sid" value="#{wizard.sid}"/&gt;
&lt;h:inputHidden id="h_jdbcPort" value="#{wizard.jdbcPort}"/&gt;
&lt;/c:if&gt;
</pre>
		<span class="bodycopy">所有的向导部分都可以在 JSF 页面中组装，如 step1.jsp：</span>
		<pre>&lt;!-- step1.jsp --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;
&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;tags:pageTemplate step="1" 
title="Create Database Connection - Step 1"&gt;
&lt;af:form id="form1"&gt;
&lt;tags:formTemplate&gt;
&lt;tags:form1/&gt;
&lt;/tags:formTemplate&gt;
&lt;tags:stepButtons/&gt;
&lt;tags:hiddenData/&gt;
&lt;/af:form&gt;
&lt;/tags:pageTemplate&gt;
</pre>
		<span class="bodycopy">step2.jsp 和 step3.jsp 页面与 step1.jsp 非常类似。作为练习，您可以尝试为这些页面构建一个模板，从而将这些页面都减少为四行代码。confirm.jsp 页面将一起显示所有三个表单，但组件在只读模式下工作，从而占用的屏幕空间更少并且无需用户交互：</span>
		<pre>&lt;!-- confirm.jsp --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;
&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;tags:pageTemplate step="4" title="Confirm Connection Parameters"&gt;
&lt;af:form id="form4"&gt;
&lt;tags:formTemplate&gt;
&lt;tags:form1/&gt;
&lt;tags:form2/&gt;
&lt;tags:form3/&gt;
&lt;/tags:formTemplate&gt;
&lt;tags:stepButtons/&gt;
&lt;tags:hiddenData/&gt;
&lt;/af:form&gt;
&lt;/tags:pageTemplate&gt;
</pre>
		<span class="bodycopy">
				<strong>基于菜单的界面。</strong>假定用户逐步浏览向导的页面，提供所有需要的信息。如果用户需要在以后修改某些地方，那么他应当不需要再次浏览所有的向导页面。相反，用户界面将让用户直接转至必须修改信息的表单。menuTabs.tag 文件使用相同名称的 Oracle ADF Faces 组件来构建菜单：</span>
		<pre>&lt;!-- menuTabs.tag --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:menuTabs&gt;
&lt;af:commandMenuItem text="Name and Type" action="tab1"
selected="#{wizard.currentStep == 1}"/&gt;
&lt;af:commandMenuItem text="Authentication" action="tab2"
selected="#{wizard.currentStep == 2}"/&gt;
&lt;af:commandMenuItem text="Connection" action="tab3"
selected="#{wizard.currentStep == 3}"/&gt;
&lt;/af:menuTabs&gt;
</pre>
		<span class="bodycopy">菜单的导航规则在 faces-config.xml 中定义：</span>
		<pre>&lt;faces-config&gt;
    ...
&lt;navigation-rule&gt;
&lt;from-view-id&gt;*&lt;/from-view-id&gt;
&lt;navigation-case&gt;
&lt;from-outcome&gt;tab1&lt;/from-outcome&gt;
&lt;to-view-id&gt;/tab1.jsp&lt;/to-view-id&gt;
&lt;/navigation-case&gt;
&lt;/navigation-rule&gt;
    ...
&lt;navigation-rule&gt;
&lt;from-view-id&gt;*&lt;/from-view-id&gt;
&lt;navigation-case&gt;
&lt;from-outcome&gt;tab3&lt;/from-outcome&gt;
&lt;to-view-id&gt;/tab3.jsp&lt;/to-view-id&gt;
&lt;/navigation-case&gt;
&lt;/navigation-rule&gt;
    ...
&lt;/faces-config&gt;
</pre>
		<span class="bodycopy">当用户单击菜单的标签时，表单数据将被提交给 Web 服务器，在该服务器上 JSF 框架将更新 Backing Bean。如果用户想更新表单而不改变当前的标签，那么需要使用提交按钮。submitButton.tag 文件提供了提交按钮：</span>
		<pre>&lt;!-- submitButton.tag --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;

&lt;af:commandButton id="command" text="Submit" action="submitAction"/&gt;
</pre>
		<span class="bodycopy">tab1.jsp、tab2.jsp 和 tab3.jsp 文件将把菜单附加到向导的表单上。下面是 tab1.jsp：</span>
		<pre>&lt;!-- tab1.jsp --&gt;

&lt;%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %&gt;
&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;tags:pageTemplate step="1" title="Edit Database Connection"&gt;
&lt;af:form id="form1"&gt;
&lt;tags:menuTabs/&gt;
&lt;tags:formTemplate&gt;
&lt;tags:form1/&gt;
&lt;/tags:formTemplate&gt;
&lt;tags:submitButton/&gt;
&lt;tags:hiddenData/&gt;
&lt;/af:form&gt;
&lt;/tags:pageTemplate&gt;
</pre>
		<span class="parahead1">使显示逻辑可重用</span>
		<p>
				<span class="bodycopy">如果您必须使用 JSF 和 Oracle ADF Faces 从头开始构建新的页面，那么一切都没什么问题。但是，对于包含了以 Java 代码形式存在的显示逻辑的旧 JSP 页面，该如何处理呢？维护这些页面困难重重，并且，若不将显示代码从 JSP 页面中分离出来，则无法对其进行重用。此外，Java 代码和 HTML 标记不应混合在同一个页面中，因为这将使 Java 开发人员和 Web 设计人员无法轻松地展开并行工作。</span>
		</p>
		<p>
		</p>
		<span class="bodycopy">JSF 和 Oracle ADF Faces 组件解决了多数情况下的此种问题，因为 Web 页面将使用这两个框架提供的标记来构建，同时将 Java 代码放到了 Backing Bean 中。JSTL 和其他的标记库也非常有用，但有时您必须只能使用 Java 代码来动态生成内容。</span>
		<p>
		</p>
		<span class="bodycopy">一种好的解决方案是使用 JSP 2.0 提供的 Simple Tags API 来构建标记库。该 API 使您能够开发标记处理器类，但您必须维护一个单独的 XML 文件（称为标记库描述符 (TLD)），该文件定义标记名称、它们的属性等。如果这听起来太复杂，那么您可以简单地将 Java 代码移到使用 JSP 语法的标记文件中，让应用服务器生成标记处理器类和 TLD 文件。让我们看一下名为 oldCode.jsp 的 JSP 页面，它混合了 Java 和 HTML。该页面将读取一个文本文件（其路径将作为一个请求参数提供）并显示文件的内容（包括行号）。当您构建演示应用程序并想显示代码时，这将非常有用。</span>
		<p>
		</p>
		<span class="bodycopy">重要注意事项！请勿在生产环境中使用本部分的示例（oldCode.jsp 和 newCode.jsp），因为它们可能会泄漏应用程序的源代码。</span>
		<p>
		</p>
		<span class="bodycopy">oldCode.jsp 页面使用 java.io API 来读取文本文件。它将在 JSP 页面范围中为每一行文本创建两个名为 <tt>lineText</tt> 和 <tt>lineNo</tt> 的变量。行号将用 JSTL 的 <tt>&lt;fmt:formatNumber&gt;</tt> 标记来进行格式化，文本将通过 <tt>&lt;c:out&gt;</tt> 标记进行显示：</span>
		<pre>&lt;!-- oldCode.jsp --&gt;

&lt;%@ page import="java.io.*" %&gt;
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;

&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;${param.path}&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;

&lt;c:if test="${empty param.path}"&gt;
&lt;P&gt;The &lt;CODE&gt;path&lt;/CODE&gt; parameter wasn't specified.
&lt;/c:if&gt;

&lt;c:if test="${!empty param.path}"&gt;
&lt;P&gt;&lt;B&gt;&lt;CODE&gt;${param.path}&lt;/CODE&gt;&lt;/B&gt;
&lt;%
String path = application.getRealPath(
request.getParameter("path"));
BufferedReader in = new BufferedReader(new FileReader(path));
try {
int lineNo = 0;
String lineText;
while ((lineText = in.readLine()) != null) {
lineNo++;
pageContext.setAttribute("lineText", lineText);
pageContext.setAttribute("lineNo",
new Integer(lineNo));
%&gt;
&lt;fmt:formatNumber var="fmtLineNo"
value="${lineNo}" minIntegerDigits="3"/&gt;
&lt;PRE&gt;${fmtLineNo}  &lt;c:out value="${lineText}"/&gt;&lt;/PRE&gt;
&lt;%
        }
} finally {
in.close();
    }
%&gt;
&lt;/c:if&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</pre>
		<span class="bodycopy">来自前一个页面示例的全部 Java 代码都可以移到一个名为 readTextFile.tag 的标记文件中。只需进行少许修改：您必须使用 <tt>&lt;%@tag%&gt;</tt> 指令和 jspContext 隐式对象，而不是 <tt>&lt;%@page%&gt;</tt> 和 pageContext。您还必须使用 JSP 指令来声明属性和变量：</span>
		<pre>&lt;!-- readTextFile.tag --&gt;

&lt;%@ tag import="java.io.*" %&gt;
&lt;%@ attribute name="path" required="true" %&gt;
&lt;%@ variable name-given="lineText" scope="NESTED" %&gt;
&lt;%@ variable name-given="lineNo" scope="NESTED" %&gt;
&lt;%
String path = application.getRealPath(
(String) jspContext.getAttribute("path"));
BufferedReader in = new BufferedReader(new FileReader(path));
try {
int lineNo = 0;
String lineText;
while ((lineText = in.readLine()) != null) {
lineNo++;
jspContext.setAttribute("lineText", lineText);
jspContext.setAttribute("lineNo",
new Integer(lineNo));
%&gt;
&lt;jsp:doBody/&gt;
&lt;%
        }
} finally {
in.close();
    }
%&gt;
</pre>
		<span class="bodycopy">您可以在任何需要逐行处理文本文件的 JSP 页面中使用 readTextFile.tag 文件。每一个页面都可以对文本行执行任何需要的操作，因为该标记文件使用了 <tt>&lt;jsp:doBody/&gt;</tt>，从而允许 JSP 页面将处理当前行的代码放到 <tt>&lt;tags:readTextFile&gt;</tt> 和 <tt>&lt;/tags:readTextFile&gt;</tt> 之间。newCode.jsp 页面的功能与旧样式的示例相同，但它没有混合 Java 和 HTML：</span>
		<pre>&lt;!-- newCode.jsp --&gt;

&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;
&lt;%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %&gt;

&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;${param.path}&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;

&lt;c:if test="${empty param.path}"&gt;
&lt;P&gt;The &lt;CODE&gt;path&lt;/CODE&gt; parameter wasn't specified.
&lt;/c:if&gt;

&lt;c:if test="${!empty param.path}"&gt;
&lt;P&gt;&lt;B&gt;&lt;CODE&gt;${param.path}&lt;/CODE&gt;&lt;/B&gt;
&lt;tags:readTextFile path="${param.path}"&gt;
&lt;fmt:formatNumber var="fmtLineNo"
value="${lineNo}" minIntegerDigits="3"/&gt;
&lt;PRE&gt;${fmtLineNo}  &lt;c:out value="${lineText}"/&gt;&lt;/PRE&gt;
&lt;/tags:readTextFile&gt;
&lt;/c:if&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</pre>
		<span class="bodycopy">正如您所见，修改旧的 JSP 页面以便可以重用代码并不是很难。维护也变得更加容易。</span>
		<p>
				<span class="parahead1">随意重用</span>
		</p>
		<p>
		</p>
		<span class="bodycopy">重复的代码或内容是最令人头疼的事情。有时它可能是一种容易的解决方案，但修改应用程序变得更加困难，这意味着从长远来看，您将不能适应新的用户需求或快速地修复问题。在理想情况下，应用程序不应包含相同代码的多个版本，Web 内容不应被复制和粘贴。</span>
<img src ="http://www.blogjava.net/kingwell/aggbug/45457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingwell/" target="_blank">KingWell</a> 2006-05-10 14:53 <a href="http://www.blogjava.net/kingwell/articles/45457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>