Velocity空間

          快速構建JAVA應用
          隨筆 - 11, 文章 - 15, 評論 - 5, 引用 - 0
          數據加載中……

          CHAPTER 6 理解模板和上下文

          在之前的章節里,我們介紹了VelocityHello World 示例,并從中看到了Velocity的一些簡單應用,是時候學習Velocity核心組件了。本章將深入學習模板和上下文。后續章節將在本章知識的基礎上,詳細討論Velocity的引用、指令和宏。

          使用模板

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


          Figure 6.1
          An example of a general template.

          Velocity俱樂部應用程序的內容包含了兩個部分的內容:靜態內容和動態內容。靜態內容由標題、標簽(如:First Name:, Last Name: 等)、問題和全部的布局等組成;動態內容就是表單中的下劃線部分,用于申請人填寫個人信息。

          普通模板定義(如Velocity俱樂部應用程序的模板)和真實的Velocity模板最大的不同之處在于,Velocity的動態內容是指定的。當應用程序窗體依賴于插入空白部分的動態內容時,Velocity使用了引用的概念?,F在,你可以簡單想一下,Velocity引用就好比某種實體引用了一個存儲在別處的值。比如,基于Velocity引用的基本語法,一個模板或許包含了一個名叫$name的實體和字符串“John Doe”對應,該字符串一般存儲在模板外的某個地方(如數據庫)。如果你對這個含糊的定義有些困惑的話,請不要太擔心,我們將在下章詳細討論引用的細節,重要的是要緊記Velocity引用從本質上來講是作為模板中動態內容的占位符。如果你想把這個俱樂部應用程序轉換成Velocity模板,只需要把那些空白部分替換成適當的Velocity引用就行。如何制定一個適當的Velocity引用定義?首先定義要符合Velocity引用語法,其次是設計者和編程人員要對模板引用的名稱進行約定。一種可能的模式實現詳見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模板從本質上講,和應用程序原來的窗體是相同的,除了空白地方被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"

          那么輸出結果為(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.

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

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

          #*

          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.

          上下文

          在前面的部分,我們為應用程序模板里的引用指定了字符串。然而,我們曾經提到過把Velocity引用和字符串值綁定在一起的機制,這就是Velocity上下文的概念。上下文在應用程序端表現為通過在org.apache.velocity.context中定義的上下文接口。上下文通過關鍵字映射存儲的對象。上下文只存儲java.lang.Object類型的對象,該對象的關鍵字類型為java.lang.String。

          引用除了具有$前綴外,用于關鍵字的字符串值和用于引用名稱的值必須相同。例如,字符串值"John"在上下文里對應關鍵字firstName,模板將通過引用$firstName訪問字符串值"John"。大體上,模板的動態內容通過關鍵字在上下文中查找內容來指定。$前綴讓模板引擎知道$后的文本就是上下文中關鍵字的名稱。

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

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

          在一個典型的應用程序里,上下文表現為一個org.apache.velocity.VelocityContext的實例,它實現了上下文接口。上下文實例存儲了對象"John" "Doe",對應關鍵字分別為"first-Name" "lastName",代碼如下:

          VelocityContext context = new VelocityContext();

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

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

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

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

          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()方法允許通過關鍵字檢索上下文,返回true(表示找到了和關鍵字對應的對象)和false。getKeys()方法返回所有關鍵字的列表,要注意的是返回的類型為對象數組,而不是字符串數組。remove()方法用于移除關鍵字對應的對象,同時也將移除關鍵字,返回值為被移除對象的值。get()方法允許編程人員通過關鍵字訪問對象。

          Putting the Pieces Together放置組合體?

          既然你已經對模板和上下文在Velocity中扮演的角色有了大概了解,那就讓我們把組合體放到應用程序的窗體里,對 Velocity模板進行處理,以顯示俱樂部Velocity成員應用程序的窗體。既然本應用示例的真實意圖是用于闡明Velocity應用的基礎結構,那我們就讓它的邏輯盡量簡單。特別的,本示例中,動態組件的最終內容是來自硬編碼的字符串,在現實的應用程序中,這些動態內容更多來自數據庫或產生于別的途徑。

          在查看應用程序代碼之前,先來了解大概的順序。把Listing 6.1定義的模板作為起點,開發一個處理模板的應用程序,用Velocity上下文中適當的文本替換所有的引用,最終內容(包含直接取自模板的靜態內容和取自上下文的動態內容)將被應用程序輸出,結果詳見Listing 6.2。

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

          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 );

          }

          // 創建上下文

          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.

          我們現在來討論一下這個示例,可以說是把它放到放大鏡下進行觀察。代碼開始于import語句,你可以看到java.io.StringWriter被導入到程序中,雖然StringWriter類并非必須,但一般情況下,導入StringWriter類是適當的,因為模板和上下文的合并處理通常需要一個java.io.Writer的衍生對象或衍生類。導入的第一個Velocity包是org.apache.velocity.Template,其相應的模板類提供了一個由應用程序使用的、常駐內存的模板。接著導入的是org.apache.velocity.VelocityContext類,用于處理Velocity上下文。之后,應用程序導入了org.apache.velocity.app.Velocity類,它提供了獨立模式的Velocity模板引擎。通過org.apache.velocity.app.VelocityEngine類(將在Chapter 10討論)可實現非獨立模式Velocity模板引擎(non-Singleton implementation)。最后導入的是org.apache.velocity.exception.*,它提供了一個訪問各種Velocity異常類的能力。

          Import語句之后,將進入模板處理的執行代碼。正如前面提及的,緊隨其后的是Velocity應用的通用代碼。第一個元素是初始化模板引擎,使用的是獨立模式,初始化操作非常容易,僅僅調用靜態方法Velocity.init()即可,如果因其他原因造成初始化失敗,將拋出一個java.lang.Exception類型的通用異常;如果使用的是非獨立模式,你需要創建VelocityEngine的實例來代替它,并調用該實例的init()方法,而不是直接調用Velocity的靜態方法Velocity.init()。

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

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

          在正確獲取模板之后,你就可以創建和組裝上下文了。首先要創建一個VelocityContext實例。上下文的組裝通過實例調用VelocityContextput()方法來完成,每一個關鍵字對應一個JAVA對象。在一個典型的Velocity應用中,很多處理都將圍繞上下文組裝這個操作,這里是生成讓模板設計者可用的動態內容的地方,所有的數據中轉和處理都將在這里完成。

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

          最后,將輸出最后的處理結果。一旦你把StringWriter用于合并操作,render操作將不再調用WritertoString()方法,而是直接輸出到適當的位置,比如瀏覽器。輸出結果見Listing 6.2

          本章小節和下章介紹

          這一章全面介紹了Velocity模板和上下文基礎,我們通過一個示例應用示范了如何使用模板和上下文,并且對他們進行了詳細的討論。通過這些內容的學習,你應該可以設計一些基本的模板或開發簡單的模板處理應用程序了。然而,在使用Velocity進行更多高級模板處理任務時,仍有許多障礙,要清除這些障礙,我們首先需要對Velocity引用進行更廣泛的研究,這就是我們下一章將要學習的內容。

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

          評論

          # re: CHAPTER 6 理解模板和上下文  回復  更多評論   

          很細致,寫得很好
          2012-08-13 16:38 | 各個
          主站蜘蛛池模板: 重庆市| 微博| 宁安市| 怀仁县| 延吉市| 沾化县| 司法| 苍梧县| 融水| 涟源市| 武宣县| 阳原县| 吴旗县| 元氏县| 永新县| 淄博市| 三江| 高碑店市| 绥江县| 萨嘎县| 嘉禾县| 炎陵县| 平潭县| 繁峙县| 司法| 黄龙县| 罗江县| 福建省| 娱乐| 区。| 如东县| 苏尼特左旗| 潜山县| 海城市| 莒南县| 陕西省| 繁昌县| 长武县| 民和| 平湖市| 清流县|