Velocity空間

          快速構(gòu)建JAVA應(yīng)用
          隨筆 - 11, 文章 - 15, 評(píng)論 - 5, 引用 - 0
          數(shù)據(jù)加載中……

          CHAPTER 6 理解模板和上下文

          在之前的章節(jié)里,我們介紹了VelocityHello World 示例,并從中看到了Velocity的一些簡(jiǎn)單應(yīng)用,是時(shí)候?qū)W習(xí)Velocity核心組件了。本章將深入學(xué)習(xí)模板和上下文。后續(xù)章節(jié)將在本章知識(shí)的基礎(chǔ)上,詳細(xì)討論Velocity的引用、指令和宏。

          使用模板

          Velocity是一個(gè)模板引擎,使用Velocity進(jìn)行開發(fā)將是一件非常愉快的事。但是如何來(lái)制作模板呢?Velocity的模板定義和許多領(lǐng)域使用的模板概念相似,比如許多文本處理包為通用文檔提供了文檔處理的預(yù)定義包,如office備忘錄等,這些都與Velocity模板的概念相當(dāng)接近。Velocity俱樂部應(yīng)用程序窗體(詳見Figure 6.1)是常規(guī)模板的另一個(gè)例子。


          Figure 6.1
          An example of a general template.

          Velocity俱樂部應(yīng)用程序的內(nèi)容包含了兩個(gè)部分的內(nèi)容:靜態(tài)內(nèi)容和動(dòng)態(tài)內(nèi)容。靜態(tài)內(nèi)容由標(biāo)題、標(biāo)簽(如:First Name: Last Name: 等)、問題和全部的布局等組成;動(dòng)態(tài)內(nèi)容就是表單中的下劃線部分,用于申請(qǐng)人填寫個(gè)人信息。

          普通模板定義(如Velocity俱樂部應(yīng)用程序的模板)和真實(shí)的Velocity模板最大的不同之處在于,Velocity的動(dòng)態(tài)內(nèi)容是指定的。當(dāng)應(yīng)用程序窗體依賴于插入空白部分的動(dòng)態(tài)內(nèi)容時(shí),Velocity使用了引用的概念。現(xiàn)在,你可以簡(jiǎn)單想一下,Velocity引用就好比某種實(shí)體引用了一個(gè)存儲(chǔ)在別處的值。比如,基于Velocity引用的基本語(yǔ)法,一個(gè)模板或許包含了一個(gè)名叫$name的實(shí)體和字符串“John Doe”對(duì)應(yīng),該字符串一般存儲(chǔ)在模板外的某個(gè)地方(如數(shù)據(jù)庫(kù))。如果你對(duì)這個(gè)含糊的定義有些困惑的話,請(qǐng)不要太擔(dān)心,我們將在下章詳細(xì)討論引用的細(xì)節(jié),重要的是要緊記Velocity引用從本質(zhì)上來(lái)講是作為模板中動(dòng)態(tài)內(nèi)容的占位符。如果你想把這個(gè)俱樂部應(yīng)用程序轉(zhuǎn)換成Velocity模板,只需要把那些空白部分替換成適當(dāng)?shù)?/span>Velocity引用就行。如何制定一個(gè)適當(dāng)?shù)?/span>Velocity引用定義?首先定義要符合Velocity引用語(yǔ)法,其次是設(shè)計(jì)者和編程人員要對(duì)模板引用的名稱進(jìn)行約定。一種可能的模式實(shí)現(xiàn)詳見Listing 6.1

          CLUB VELOCITY APPLICATION

          First Name: $firstName

          Last Name: $lastName

          Address: $streetAddress

          City: $city State: $state Zip: $zip

          Phone Number: $phoneNumber

          Email Address: $emailAddress

          Occupation: $occupation

          Other Interests: $otherInterests

          Is this a new membership request or a renewal? $appType

          How long have you been using Velocity? $useTime

          Do you use Velocity for work or play? $useType

          Do you want to receive our newsletter? $wantNewsletter

          Listing 6.1 The club application form after conversion to a Velocity template.

          正如你所看見的一樣,Velocity模板從本質(zhì)上講,和應(yīng)用程序原來(lái)的窗體是相同的,除了空白地方被Velocity引用替換外。下面我們先假定引用的值分別如下:

          $firstName => "John"

          $lastName => "Doe"

          $streetAddress => "123 Jane Ave. "

          $city => "Azusa"

          $state => "CA"

          $zip => "91702"

          $phoneNumber => "626-555-1234"

          $emailAddress => "john@nodom.com"

          $occupation => "Web Developer"

          $otherInterests => "Hiking,Biking"

          $appType => "New"

          $useTime => "6 months"

          $useType => "Work"

          $wantNewsletter => "Yes"

          那么輸出結(jié)果為(Listing 6.2):

          CLUB VELOCITY APPLICATION

          First Name: John

          Last Name: Doe

          Address: 123 Jane Ave.

          City: Azusa State: CA Zip: 91702

          Phone Number: 626-555-1234

          Email Address: john@nodom.com

          Occupation: Web Developer

          Other Interests: Hiking,Biking

          Is this a new membership request or a renewal? New

          How long have you been using Velocity? 6 months

          Do you use Velocity for work or play? Work

          Do you want to receive our newsletter? Yes

          Listing 6.2 Sample output for the Velocity template version of the club application form.

          現(xiàn)在,你已經(jīng)明白了如何從模板輸出內(nèi)容,讓我們快速考慮一下在處理過程中如何防止模板的部分內(nèi)容被輸出。具有這樣的處理能力不僅對(duì)你有用,對(duì)你的同事也有使用,一句注解或幾句模板代碼的說明能讓你在閱讀代碼時(shí)更易理解你當(dāng)時(shí)設(shè)計(jì)代碼的意圖。另外,也具有在調(diào)試session期間選擇性地讓部分模板內(nèi)容停止輸出的能力,有時(shí)還需要明確地停止Velocity的默認(rèn)處理行為。Velocity支持模板注釋機(jī)制提供了這種能力,它包括塊注釋和單行注釋。塊注釋由#*開始,到 *#結(jié)束,所有在它們之間的內(nèi)容將被模板引擎丟棄。單行注釋由兩個(gè)##開始的。Listing 6.3提供了一些模板注釋示例。

          之前的內(nèi)容描述了Velocity的概貌,但我們忽視了幾個(gè)重要的主題。首先,這將意味著Velocity引用比我們目前透露的還要多。此外,Velocity還提供了許多可直接控制模板內(nèi)容的指令和宏。指令提供了流控制、文件包含和引用處理功能;宏為模板代碼提供了一個(gè)強(qiáng)大的可再使用機(jī)制。我們將在隨后的章節(jié)里討論這些主題。

          #*

          This is a block comment. It is being used to point out

          that this listing is intended to demonstrate the use of

          comments in Velocity templates.

          *#

          ##This line is not rendered.

          This part is rendered,## but this part is not.

          If only the #*middle*# bit needs to be commented out, a

          block comment will do the job.

          Listing 6.3 Velocity comment examples.

          上下文

          在前面的部分,我們?yōu)閼?yīng)用程序模板里的引用指定了字符串。然而,我們?cè)?jīng)提到過把Velocity引用和字符串值綁定在一起的機(jī)制,這就是Velocity上下文的概念。上下文在應(yīng)用程序端表現(xiàn)為通過在org.apache.velocity.context中定義的上下文接口。上下文通過關(guān)鍵字映射存儲(chǔ)的對(duì)象。上下文只存儲(chǔ)java.lang.Object類型的對(duì)象,該對(duì)象的關(guān)鍵字類型為java.lang.String

          引用除了具有$前綴外,用于關(guān)鍵字的字符串值和用于引用名稱的值必須相同。例如,字符串值"John"在上下文里對(duì)應(yīng)關(guān)鍵字firstName,模板將通過引用$firstName訪問字符串值"John"。大體上,模板的動(dòng)態(tài)內(nèi)容通過關(guān)鍵字在上下文中查找內(nèi)容來(lái)指定。$前綴讓模板引擎知道$后的文本就是上下文中關(guān)鍵字的名稱。

          Velocity上下文和對(duì)象進(jìn)行組裝有三條途徑。首先,Velocity自身可以向上下文中插入有效的值,比如使用#foreach指令進(jìn)行反復(fù)計(jì)算。第二,#set指令允許模板向上下文中直接插入值。最后,更為重要的是,Velocity上下文接口允許編程人員使用雙方約定的關(guān)鍵字名稱,利用設(shè)計(jì)人員需要的數(shù)據(jù)來(lái)組裝上下文。

          我們只講解最后一種情況。

          在一個(gè)典型的應(yīng)用程序里,上下文表現(xiàn)為一個(gè)org.apache.velocity.VelocityContext的實(shí)例,它實(shí)現(xiàn)了上下文接口。上下文實(shí)例存儲(chǔ)了對(duì)象"John" "Doe",對(duì)應(yīng)關(guān)鍵字分別為"first-Name" "lastName",代碼如下:

          VelocityContext context = new VelocityContext();

          context.put( "firstName", "John" );

          context.put( "lastName", "Doe" );

          創(chuàng)建好后,模板就可以使用引用$firstName $lastName來(lái)訪問對(duì)象"John""Doe"。在這種情況下,雖然上下文中的對(duì)象已經(jīng)是JAVA字符串類型,但是它比其他的JAVA類型(比如Integer Float)更容易接受的。在其他情況下,Velocity使用對(duì)象的toString()方法來(lái)生成一個(gè)字符串,以用于顯示輸出。當(dāng)然,它也可能是比簡(jiǎn)單值更復(fù)雜的一個(gè)對(duì)象,這通常發(fā)生在模板的需求包含了高級(jí)功能的情況下,這時(shí)只能通過對(duì)象的方法存儲(chǔ)在上下文中。最后一種情況的細(xì)節(jié)將在Chapter 7討論(Velocity引用)。

          除了組裝上下文外,上下文接口還允許編程人員對(duì)上下文進(jìn)行查詢和更深入的操作,提供的附加方法如下:

          boolean containsKey( java.lang.Object key )

          java.lang.Object[] getKeys()

          java.lang.Object remove( java.lang.Object key )

          java.lang.Object get( java.lang.String key )

          containsKey()方法允許通過關(guān)鍵字檢索上下文,返回true(表示找到了和關(guān)鍵字對(duì)應(yīng)的對(duì)象)和falsegetKeys()方法返回所有關(guān)鍵字的列表,要注意的是返回的類型為對(duì)象數(shù)組,而不是字符串?dāng)?shù)組。remove()方法用于移除關(guān)鍵字對(duì)應(yīng)的對(duì)象,同時(shí)也將移除關(guān)鍵字,返回值為被移除對(duì)象的值。get()方法允許編程人員通過關(guān)鍵字訪問對(duì)象。

          Putting the Pieces Together放置組合體?

          既然你已經(jīng)對(duì)模板和上下文在Velocity中扮演的角色有了大概了解,那就讓我們把組合體放到應(yīng)用程序的窗體里,對(duì) Velocity模板進(jìn)行處理,以顯示俱樂部Velocity成員應(yīng)用程序的窗體。既然本應(yīng)用示例的真實(shí)意圖是用于闡明Velocity應(yīng)用的基礎(chǔ)結(jié)構(gòu),那我們就讓它的邏輯盡量簡(jiǎn)單。特別的,本示例中,動(dòng)態(tài)組件的最終內(nèi)容是來(lái)自硬編碼的字符串,在現(xiàn)實(shí)的應(yīng)用程序中,這些動(dòng)態(tài)內(nèi)容更多來(lái)自數(shù)據(jù)庫(kù)或產(chǎn)生于別的途徑。

          在查看應(yīng)用程序代碼之前,先來(lái)了解大概的順序。把Listing 6.1定義的模板作為起點(diǎn),開發(fā)一個(gè)處理模板的應(yīng)用程序,用Velocity上下文中適當(dāng)?shù)奈谋咎鎿Q所有的引用,最終內(nèi)容(包含直接取自模板的靜態(tài)內(nèi)容和取自上下文的動(dòng)態(tài)內(nèi)容)將被應(yīng)用程序輸出,結(jié)果詳見Listing 6.2

          符合這個(gè)目標(biāo)的應(yīng)用程序源代碼詳見Listing 6.4。這些代碼是比較典型的通用代碼,適用于多數(shù) Velocity應(yīng)用程序,它有完善的注釋。它共有六個(gè)步驟:模板引擎初始化,模板包含,上下文創(chuàng)建,上下文組裝,模板和上下文合并和內(nèi)容表示。要注意的是,這僅僅是一個(gè)通用模式,它還有一些彈性,可以進(jìn)行進(jìn)一步擴(kuò)展。但是,如果你牢牢記住了這個(gè)模式,就會(huì)少犯錯(cuò)誤。

          import java.io.StringWriter;

          import org.apache.velocity.Template;

          import org.apache.velocity.VelocityContext;

          import org.apache.velocity.app.Velocity;

          import org.apache.velocity.exception.*;

          public class ClubApp

          {

          public static void main( String[] args )

          {

          // Initialize template engine

          try

          {

          Velocity.init();

          }

          catch( Exception x )

          {

          System.err.println( "Failed to initialize Velocity: " + x );

          System.exit( 1 );

          }

          // 獲取模板

          Template clubTemplate = null;

          try

          {

          clubTemplate = Velocity.getTemplate( "ClubApp.vm" );

          }

          catch( ResourceNotFoundException rnfX )

          {

          System.err.println( "Template not found: " + rnfX );

          System.exit( 1 );

          }

          catch( ParseErrorException peX )

          {

          System.err.println( "Failed to parse template: " + peX );

          System.exit( 1 );

          }

          catch( Exception x )

          {

          System.err.println( "Failed to initialize template: " + x );

          System.exit( 1 );

          }

          // 創(chuàng)建上下文

          VelocityContext context = new VelocityContext();

          // Populate context

          context.put( "firstName", "John" );

          context.put( "lastName", "Doe" );

          context.put( "streetAddress", "123 Jane Ave." );

          context.put( "city", "Azusa" );

          context.put( "state", "CA" );

          context.put( "zip", "91702" );

          context.put( "phoneNumber", "626-555-1234" );

          context.put( "emailAddress", "john@nodom.com" );

          context.put( "occupation", "Web Developer" );

          context.put( "otherInterests", "Hiking,Biking" );

          context.put( "appType", "New" );

          context.put( "useTime", "6 months" );

          context.put( "useType", "Work" );

          context.put( "wantNewsletter", "Yes" );

          // Merge template and context

          StringWriter writer = new StringWriter();

          try

          {clubTemplate.merge( context, writer );}

          catch( ResourceNotFoundException rnfX )

          {

          System.err.println( "Template not found on merge: " + rnfX );

          System.exit( 1 );

          }

          catch( ParseErrorException peX )

          {

          System.err.println( "Failed to parse template on merge: " + peX );

          System.exit( 1 );

          }

          catch( MethodInvocationException miX )

          {

          System.err.println( "Application method exception: " + miX );

          System.exit( 1 );

          }

          catch( Exception x )

          {

          System.err.println( "Failed to merge template: " + x );

          System.exit( 1 );

          }

          // Render merged content

          System.out.println( writer.toString() );

          }

          }

          Listing 6.4 An application for processing the club membership form.

          我們現(xiàn)在來(lái)討論一下這個(gè)示例,可以說是把它放到放大鏡下進(jìn)行觀察。代碼開始于import語(yǔ)句,你可以看到java.io.StringWriter被導(dǎo)入到程序中,雖然StringWriter類并非必須,但一般情況下,導(dǎo)入StringWriter類是適當(dāng)?shù)模驗(yàn)槟0搴蜕舷挛牡暮喜⑻幚硗ǔP枰粋€(gè)java.io.Writer的衍生對(duì)象或衍生類。導(dǎo)入的第一個(gè)Velocity包是org.apache.velocity.Template,其相應(yīng)的模板類提供了一個(gè)由應(yīng)用程序使用的、常駐內(nèi)存的模板。接著導(dǎo)入的是org.apache.velocity.VelocityContext類,用于處理Velocity上下文。之后,應(yīng)用程序?qū)肓?/span>org.apache.velocity.app.Velocity類,它提供了獨(dú)立模式的Velocity模板引擎。通過org.apache.velocity.app.VelocityEngine類(將在Chapter 10討論)可實(shí)現(xiàn)非獨(dú)立模式Velocity模板引擎(non-Singleton implementation)。最后導(dǎo)入的是org.apache.velocity.exception.*,它提供了一個(gè)訪問各種Velocity異常類的能力。

          Import語(yǔ)句之后,將進(jìn)入模板處理的執(zhí)行代碼。正如前面提及的,緊隨其后的是Velocity應(yīng)用的通用代碼。第一個(gè)元素是初始化模板引擎,使用的是獨(dú)立模式,初始化操作非常容易,僅僅調(diào)用靜態(tài)方法Velocity.init()即可,如果因其他原因造成初始化失敗,將拋出一個(gè)java.lang.Exception類型的通用異常;如果使用的是非獨(dú)立模式,你需要?jiǎng)?chuàng)建VelocityEngine的實(shí)例來(lái)代替它,并調(diào)用該實(shí)例的init()方法,而不是直接調(diào)用Velocity的靜態(tài)方法Velocity.init()

          初始化模板引擎之后,應(yīng)用程序下一步將獲取一個(gè)常駐內(nèi)存的模板。隨著模板引擎的初始化,這一步完成時(shí)創(chuàng)建的manner(依賴于獨(dú)立或非獨(dú)立模式)就可以使用了。在獨(dú)立模式下,通過調(diào)用Velocity類的靜態(tài)方法getTemplate(),將返回一個(gè)模板的實(shí)例。這個(gè)方法通過模板名稱參數(shù)獲得并加載了一個(gè)模板文件,本例中是ClubApp.vm。注意,雖然VM是公認(rèn)的Velocity模板文件標(biāo)準(zhǔn)后綴,你也不必遵守這個(gè)約定,你可以將其改成其他的后綴,比如:XXX

          如果一個(gè)非獨(dú)立模式被替換用于模板獲取,剩余的處理必須相同。關(guān)鍵差別在于調(diào)用getTemplate()方法,非獨(dú)立模式只能用VelocityEngine創(chuàng)建的實(shí)例來(lái)調(diào)用,不能采用調(diào)用Velocity靜態(tài)方法的方式來(lái)調(diào)用。不管采用哪種模式,都必須增加相同的異常處理集,包括ResourceNotFoundException ParseErrorExceptionException。前兩個(gè)Velocity異常在org.apache.velocity.exception包里指定,最后一個(gè)是標(biāo)準(zhǔn)的java.lang. Exception包。ResourceNotFoundException異常在Velocity不能定位指定模板文件時(shí)拋出;ParseErrorException異常在Velocity不能解析模板時(shí)拋出;最后,Exception異常在模板獲取期間,發(fā)生任何其他問題時(shí)拋出。

          在正確獲取模板之后,你就可以創(chuàng)建和組裝上下文了。首先要?jiǎng)?chuàng)建一個(gè)VelocityContext實(shí)例。上下文的組裝通過實(shí)例調(diào)用VelocityContextput()方法來(lái)完成,每一個(gè)關(guān)鍵字對(duì)應(yīng)一個(gè)JAVA對(duì)象。在一個(gè)典型的Velocity應(yīng)用中,很多處理都將圍繞上下文組裝這個(gè)操作,這里是生成讓模板設(shè)計(jì)者可用的動(dòng)態(tài)內(nèi)容的地方,所有的數(shù)據(jù)中轉(zhuǎn)和處理都將在這里完成。

          現(xiàn)在你手里已經(jīng)有了模板和上下文,下一步就是合并他們。這一步的作用是用從上下文中獲取的數(shù)據(jù)替換模板中的引用,該合并操作是通過調(diào)用模板實(shí)例(之前創(chuàng)建的)的merge()方法完成的。模板的merge()方法需要兩個(gè)參數(shù),第一個(gè)是上下文(實(shí)現(xiàn)了上下文接口的任何對(duì)象),這里,你需要傳遞一個(gè)Velocity-Context的實(shí)例;第二個(gè)是java.io.Writer類型的對(duì)象,或其他Writer的衍生類型對(duì)象。Writer對(duì)象用于保存模板處理的結(jié)果。

          最后,將輸出最后的處理結(jié)果。一旦你把StringWriter用于合并操作,render操作將不再調(diào)用WritertoString()方法,而是直接輸出到適當(dāng)?shù)奈恢茫热鐬g覽器。輸出結(jié)果見Listing 6.2

          本章小節(jié)和下章介紹

          這一章全面介紹了Velocity模板和上下文基礎(chǔ),我們通過一個(gè)示例應(yīng)用示范了如何使用模板和上下文,并且對(duì)他們進(jìn)行了詳細(xì)的討論。通過這些內(nèi)容的學(xué)習(xí),你應(yīng)該可以設(shè)計(jì)一些基本的模板或開發(fā)簡(jiǎn)單的模板處理應(yīng)用程序了。然而,在使用Velocity進(jìn)行更多高級(jí)模板處理任務(wù)時(shí),仍有許多障礙,要清除這些障礙,我們首先需要對(duì)Velocity引用進(jìn)行更廣泛的研究,這就是我們下一章將要學(xué)習(xí)的內(nèi)容。

          posted on 2008-10-14 08:52 KINGWEE 閱讀(845) 評(píng)論(1)  編輯  收藏 所屬分類: Velocity

          評(píng)論

          # re: CHAPTER 6 理解模板和上下文  回復(fù)  更多評(píng)論   

          很細(xì)致,寫得很好
          2012-08-13 16:38 | 各個(gè)
          主站蜘蛛池模板: 金川县| 凉山| 乃东县| 丰顺县| 静宁县| 琼海市| 连云港市| 阿合奇县| 通山县| 侯马市| 屯门区| 娄底市| 东乌| 凌源市| 平塘县| 固安县| 佛冈县| 福贡县| 绥化市| 垣曲县| 通道| 华亭县| 鹿邑县| 宜春市| 邢台县| 寿阳县| 古浪县| 璧山县| 昌邑市| 大化| 隆安县| 嘉定区| 凤翔县| 漳浦县| 皋兰县| 新郑市| 洛浦县| 松江区| 怀集县| 旬邑县| 多伦县|