1. 程式人生 > >Velocity 實現客戶端和伺服器端模板

Velocity 實現客戶端和伺服器端模板

靈活的模板引擎為 JSP 技術提供一種沒有遺產負擔的選擇

Sing Li
作家, Wrox Press
2004 年 3 月 13 日

Velocity 是一種通用的、開放原始碼的模板解決方案,可以在報告生成/資料轉換應用程式中獨立使用,也可以在 MVC 模型框架中作為檢視元件。本文中,Sing Li 介紹了 Velocity,並說明如何將其模板處理功能整合到客戶端獨立應用程式、伺服器端 Web 應用程式或者 Web 服務中。

在 HTML 或者 XML 這樣的標準表示或交換格式中,文字性資料的操作和轉換是一種頻繁而且通常非常單調的活動,每個開發人員都會遇到。模板引擎可以改善這個過程,它在模板中保留輸出中的靜態部分,而動態生成和安排變化的部分。Velocity 是一種高度實用的、開放原始碼的模板引擎,可以方便地整合到其他客戶端或伺服器端應用程式中。

對於伺服器端應用程式,如果與相容 Servlet 2.3+ 的 Web 層容器整合,Velocity 為 JSP 技術提供了一種可行的替代方案,可以強制實施表示邏輯與應用程式業務邏輯的清晰劃分。事實上,Velocity 支援的模板語言非常簡單,形成的模板也十分清晰,Web 站點設計人員和樣式開發人員可以學習和維護這些模板。

本文中將考察 Velocity 的簡單模板語言、建立一些模板並將其用於獨立的客戶應用程式。然後我們將把這個模板引擎整合到 Struts MVC 框架中作為檢視元件。

基本模板引擎操作
基本模板引擎操作非常簡單。首先看一看清單 1 中的模板:

清單 1. 基本的 Velocity 模板


<html>
<head>
<title>A Template Based Page</title>
</head>
<body>
<p>This is a page generated by $generatedBy.</p>
<p>The customer's name is $customerName.</p>

</body>
</html>

這個模板是一個完整的 HTML 檔案。您可以使用文字編輯器或者喜歡的圖形化可視網頁編輯器建立該檔案。建立的簡易性是基於模板的系統的主要好處和要求。

當模板引擎執行時,清單 1 中彩色顯示的部分將被實際的資料替換。獲取資料並與模板結合的過程稱為合併。看一看清單 2 中的指令碼所表示的資料:

清單 2. 為模板合併設定資料值

#set ($generatedBy = "Velocity")
#set ($customerName = "John Doe")

現在,如果清單 1 中的模板與清單 2 中的資料合併,將得到清單 3 所示的結果:

清單 3. 合併到模板中的資料

<html>
<head>
<title>A Template Based Page</title>
</head>
<body>
<p>This is a page generated by Velocity.</p>
<p>The customer's name is John Doe.</p>

</body>
</html>

您可能發現,這種特性和字處理程式中的郵件合併功能類似。在字處理程式中,信函結構與來自郵件列表的名稱和地址合併。和郵件合併一樣,這種應用程式最適用於要合併的資料來源非常大而且有變化的情況。

從這個單純的意義上講,Velocity 是一個模板引擎。Velocity 的輸出格式僅受文字模板中所能放置的內容的限制。包括現在最流行的格式(HTML、XML、SQL,等等)。

使用 Velocity 模板語言建立模板
Velocity 模板是文字檔案(HTML、XML 等等),其中包括:

  • 照原樣合併的靜態部分
  • 將被要合併的資料替代的佔位符
  • 指令碼語言中的指示符和指令

Velocity 模板使用的指令碼語言稱為 Velocity 模板語言(VTL)。和其他指令碼語言相比,VTL 語法相對而言不是很豐富。任何具有程式設計背景的人都可以非常快地學會 VTL。

佔位符與引用
VTL 中的引用是一個命名元素,如$customerName。引用可以在 Velocity 模板中作為佔位符。在模板合併過程中,這些佔位符將被替換成相應的文字值,從而形成最終的輸出。比如,在清單 1中,我們可以看到使用了兩個 VTL 引用($generatedBy$customerName)已生成最終輸出結果。

變數在 VTL 中是一種引用型別。您可以使用#set()指示符為變數賦值。清單 4 給出了一些例子:

清單 4. 變數型別的 VTL 引用

#set( $this = "Velocity")
#set( $numericBase = 999 )
#set( $booleanCondition = true )


This page is generated using $this.
There are ($numericBase + 1) pages in total.

變數名必須從一個字母開始,因此 Velocity 很容易把變數名與模板中的貨幣符號分開(比如,$100不可能是一個變數名)。合併操作中所有的變數都被轉化成字串,可能造成一些有趣的現象。看一看清單 4 中用紅色顯示的文字。合併後的輸出如清單 5 所示:

清單 5. 合併後的模板中帶有數字值的變數

This page is generated using Velocity.
There are (999 + 1) pages in total.

因為$numericBase在合併操作中被轉化成了字串,因此不會執行算術操作。因為 VTL 專門用於模板操作而非通用的計算語言,所以只需要支援整數算術運算(儘管可以使用外掛工具進行擴充套件)。下面的指令碼說明了如何利用這種數學運算能力:


#set( $newSum = $numericBase + 1)

There are $newSum pages in total.

該模板合併後相應的輸出為:


There are 1000 pages in total.

到目前我們處理的還只有標量。要建立包含多個元素的ArrayList變數,可以使用如下的語法:


#set( $treeList = ["pine", "oak", "maple", "redwood"])

您可以使用$treeList.get(1)列表中的第二個元素。

賦值以後,$treeList就是一個基於ArrayList的變數(就像是標準 JDK 集合類中那樣)。您可以直接使用符號$treeList.get(n)訪問它的每個元素,其中的n是以 0 為基的ArrayList索引。比如,像清單 6種紅色顯示的一行所表明的那樣,$treeList.get(1)用於選擇ArrayList中第二項,即 oak。這種呼叫ArrayList類方法的語法也可用於呼叫其他變數物件的方法(更多資訊請參閱側欄中的屬性和方法參考)。

屬性和方法參考
除了在模板中設定變數之外,VTL 引用也可以是物件屬性或方法。這些物件是模板可以使用的 Java 類(一般通過上下文,參閱Velocity 上下文)。

物件屬性通過和 Javabean 類似的符號訪問。比如,可以通過 VTL 引用$customer.LastName訪問$customer物件的LastName屬性。在幕後,Velocity 使用物件的訪問器方法獲得屬性值(即呼叫物件的getLastName()方法)。

您可以用和屬性訪問類似的符號呼叫物件的方法,可以帶引數列表也可以不帶。比如,可以通過 VTL 引用$customer.getPhone("mobile"),呼叫$customer物件的getPhone()方法獲得行動電話號碼。

關於佔位符替換的一點說明:Velocity 把任何不能識別的引用作為普通文字列印,如清單 6 中下面突出顯示的兩行(藍色和紅色)所示:

清單 6. 佔位符置換

The second item in the list is $treeList.get(1).
$notDeclared is an undeclared variable.
But $!notDeclared is invisible when not declared.

VTL 支援一種靜態引用符號,以避免呈現不存在的或者空的引用。如果使用安靜引用符號,比如$!notDeclared,那麼 Velocity 將什麼也不輸出,而不是輸出完整的引用名。注意變數名前面的“!”表示這是靜態引用符號。當合並清單 6 中的模板時,兩個引用都沒有分配任何值,但是藍色顯示的引用將原樣顯示,而綠色的一個則看不到:


The second item in the list is oak.
$notDeclared is an undeclared variable.
But is invisible when not declared.

選擇性呈現和迴圈
可以使用指示符#if... #then... #else....有條件地呈現模板中特定的部分。清單 7 給出了一個例子:

清單 7. 使用 #if、#then 和 #else 有選擇地呈現

#if $customer.GoldMember 
 Thank you Mr. $customer.LastName, for flying with us. 
 Your loyal patronage is greatly appreciated.  
 This flight earns you an additional 5000 miles.
#else
 Thank you for flying with us. 
 Please consider joining our frequent flyer program.
#endif

在清單 7 的模板中,使用$customer物件的 boolean 屬性GoldMember確定在最終輸出中出現哪些資訊。對於金牌顧客,最終輸出中將呈現藍色顯示的訊息;對於其他顧客,則在最終輸出中呈現綠色顯示的訊息。

模板中經常要使用迴圈格式化表格或者列表形式的資訊。顯示的資料通常儲存在一個ArrayList引用中。在 Velocity 中唯一用於處理重複迴圈的指示符是#foreach指示符。清單 8 中的模板通過$treeList ArrayList變數演示了#foreach指示符的用法。當然,也可以使用任何其他可用的集合型別的物件引用,或者返回一個集合的物件屬性/方法引用。

清單 8. 使用 #foreach 迴圈

<table>
<tr><td>Tree Name</td></tr>
#foreach $name in $treeList
<tr><td>
     $name is a big tree!
</td></tr>
#end
</table>

$treeList中包含樹名的列表,清單 8 中的模板合併後的輸出如清單 9 所示:

清單 9. #foreach 迴圈中合併後的輸出

<table>
<tr><td>Tree Name</td></tr>
<tr><td>
     pine is a big tree!
</td></tr>
<tr><td>
     oak is a big tree!
</td></tr>
<tr><td>
     maple is a big tree!
</td></tr>
<tr><td>
     redwood is a big tree!
</td></tr>
</table>

如果從 HTML 瀏覽器中檢視,清單 9 當然就是一個包含樹名的表。

注意在#foreach迴圈體內有一個內建的計數器,可以在#foreach指示符迴圈體內通過$velocityCounter引用訪問它。預設情況下,這個計數器從 1 開始,每執行一次迴圈遞增一次。

Velocity 中的巨集
Velocity 的一個主要特性是能夠很容易地定義巨集,稱為Velocimacros。巨集使您能夠很容易地封裝和重用模板指令碼。預設情況下,巨集儲存在VM_global_library.vm檔案中。比如,考慮清單 10 中名為#showTree()的 Velocimacro:

清單 10. 定義 Velocimacro

#macro (showTree)
    #if ($treeList )
        #foreach ($e in $treeList )
            $e 
        #end
    #end
#end 

您可以呼叫#showTree()Velocimacro 並使用它列印$treeListArrayList ―― 如果這個列表已經定義的話。呼叫的語法很簡單,即#showTree()

引數化巨集也是可能的。比如,我們可以修改#showTree()巨集使其用於任何列表,如清單 11 所示:

清單 11. 帶引數的 Velocimacro

#macro (showList $val)
    #if ($val )
        #foreach ($e in $val )
            $e
        #end

    #end
#end

要使用清單 11 中的巨集呼叫$treeList,我們可以使用#showList($treeList)。這兩種情況的輸出都一樣,如清單 12 所示:

清單 12. Velocimacro 的合併輸出

     pine
     oak
     maple
     redwood

其他有趣的 VTL 細節
單行註釋或者行尾註釋從##開始,多行註釋則放在#**#之間。

在處理字串資料時,可以使用雙引號或單引號分隔。但是使用雙引號允許在分隔的字串內部對 Velocity 引用、指示符甚至 Velocimacros 求值。

Velocity 上下文
您可以把 Velocity 中的上下文看作是匯入 Java 物件,以便在 Velocity 模板內部訪問的一種方法。這種匯入必須在 Java 編碼中明確地完成。和 JSP 程式碼或者 JavaScript 不同,不存在“自然的”或“原生方式”使 Velocity 訪問任何 Java 物件。只有明確匯入的 Java 物件才能在 Velocity 模板中使用。

通過建立org.apache.velocity.context.Context類的例項可以獲得 Velocity 上下文。然後可以使用上下文的put(key,value)方法,把將要匯入供模板使用的物件附加到上下文中。key 是一個字串名,將在模板中作為可用的引用出現。在產品環境中,圖形或者 Web 設計人員可能負責建立和維護模板,而 Java 開發人員提供可以在模板中訪問的物件集。在這種情況下,設計人員和開發人員應就物件集合及其可用的屬性達成一致並互相協作。在 Velocity 上下文中附加的屬性將作為主要的介面機制。

在模板中訪問上下文屬性
看一看一個獨立解析器中包含的示例程式碼(請參閱參考資料)。可以在/code/src目錄下找到。比如,在com.ibm.dvworks.velocity.VelocityParser類中,我們已經建立並向 Velocity 上下文中添加了兩個屬性,如清單 13 所示:

清單 13. 在 VelocityParser 類中建立一個 Velocity 例項

public static void main(String[] args)    {
        VelocityParser velInstance = new VelocityParser(args[0]);
        velInstance.addToContext(  "treeFarm", 
        new String [] { "redwood", "maple", "oak", "pine" });
        velInstance.addToContext( "title", "A Tree Farm");
velInstance.addToContext( "date", new java.util.Date());
        velInstance.addToContext("fmtr", 
        new org.apache.velocity.app.tools.VelocityFormatter(
                  velInstance.getCurrentContext()));
        velInstance.processTemplate();             
    }

屬性treeFarm是一個關於樹名的ArrayListtitle屬性是一個標量字串。一旦附加到上下文中並在合併過程中傳遞,這些屬性在 Velocity 模板中立刻就變得沒有用了。清單 14 中的模板使用了這兩個屬性。您可以在/code/app/treectx.vm中找到這個例子。

清單 14. 使用 $treeFarm 上下文屬性引用

<table>
<tr><td>$title</td></tr>
#foreach $name in $treeFarm
<tr><td>
     $name is a big tree!
</td></tr>
#end
</table>

合併後的輸出如清單 15 所示:

清單 15. 模板的合併輸出

<table>
<tr><td>A Tree Farm</td></tr>
<tr><td>
     redwood is a big tree!
</td></tr>
<tr><td>
     maple is a big tree!
</td></tr>
<tr><td>
     oak is a big tree!
</td></tr>
<tr><td>
     pine is a big tree!
</td></tr>
</table>

要注意,使用$treeFarm上下文屬性引用的方法和前面分析的$treeList變數引用一致。

初始化模板引擎
分析VelocityParser類中列出的main()方法(參見清單 13)。VelocityParser建構函式建立解析器並載入模板,然後增加模板引擎要使用的屬性,最後呼叫processTemplate()合併資料和模板。我們將按照順序依次分析這些方法。

您可以在org.apache.velocity.app.Velocity類中使用靜態方法初始化 Velocity 並載入一個模板檔案。要使用的方法分別為init()getTemplate()。對init()方法的調用出現在VelocityParser類的建構函式中,如清單 16 所示:

清單 16. 在 VelocityParser 類的建構函式中初始化模板引擎

      public VelocityParser(String templateFile)  {
        try {
            Velocity.init("velocity.properties");
            mainTemplate = Velocity.getTemplate(templateFile);
         }
         catch( Exception ex ) {
                System.out.println("Error processing template file: " + templateFile );
          }
      }

在清單 16 中,對init()的呼叫建立了一個 Velocity 引擎例項。如果應用程式需要建立和管理多個 Velocity 模板引擎例項,則應使用org.apache.velocity.app.VelocityEngine類。

上下文鏈
只要呼叫org.apache.velocity.VelocityContext類的普通建構函式就可以建立直接可用的 Velocity 上下文。

Velocity 上下文可以進行連結(包裝在另一個上下文內部)。如果需要在模板中控制特定物件引用的可見性和可用性,這樣做非常有用。

Velocity 將在物件引用的所有連結上下文中搜索屬性。如果遇到重複的名稱,則使用最外層的屬性,而內部的同名屬性永遠不會被訪問。

為了連結 Velocity 上下文,要連結的上下文應該作為引數傳遞給一個新上下文的建構函式。清單 17 中VelocityParser類的過載方法addToContext()說明了這一點:

清單 17. 使用 addToContext() 方法增加上下文屬性或者上下文連結

      public void addToContext(String key, Object value) {
          if (mainContext == null)
              mainContext = new VelocityContext();
           mainContext.put(key, value);
     }
      public void addToContext(VelocityContext chainCtx) {
          mainContext = new VelocityContext(chainCtx);
      }

processTemplate()方法中,呼叫模板的merge()方法結合上下文資訊和模板生成輸出流,如清單 18 所示:

清單 18. 在 processTemplate() 方法中合併模板

     public void processTemplate() {
         try {
            BufferedWriter writer = writer = new BufferedWriter(
                new OutputStreamWriter(System.out));

            if ( mainTemplate != null)
                mainTemplate.merge(mainContext, writer);
            writer.flush();
            writer.close();
        }
        catch( Exception ex )    {
           ex.printStackTrace();
        }
    }

Velocity 作為獨立的解析器
要編譯上述示例獨立解析器,請使用安裝目錄下/code/app中的compile.bat檔案。要試驗該解析器,請使用process.bat批處理檔案,其中包括:


set VEL_HOME=/jdk1.4/vel14rc1
java -classpath ../classes;%VEL_HOME%/velocity-dep-1.4-rc1.jar 
com.ibm.dvworks.velocity.VelocityParser %1 %2 %3

注意,必須同時在compile.batprocess.bat中把環境變數VEL_HOME設定成安裝 Velocity 的目錄。在 Velocity 發行包中包含兩類不同的 JAR 檔案:velocity-dep---?.jar(其中的--?是版本號資訊)和velocity---?.jarvelocity-dep---?.jar檔案包括所有的外部依賴(Jakarta common-collections、Avalon Logkit 和 ORO 正則表示式庫),可以直接使用。如果您的 classpath 中已經有一些這樣的庫,您可能希望使用velocity---?.jar檔案來代替。如果這些 JAR 組成都不能滿足您的需要,可以很容易地按照需要的方式重新建立 Velocity。Velocity 發行包中包括一個 ant 指令碼,可以為不同的應用場景建立 7 種不同的 JAR 配置。

為了便於上手,Velocity 預設了一些預設配置屬性,對於多數應用而言,這都是合理的和可以接受的。這就避免了開發人員從一開始就忙於複雜的配置選項,讓他們能馬上體驗到這種模板引擎。

伺服器上的 Velocity 與 JSP 技術
在伺服器端可以使用 Velocity 處理模板和生成的動態內容(HTML、XML等)。這和 JSP 技術的目標非常接近。但是,JSP 模型可以毫無阻礙地訪問底層的 Servlet API 和 Java 程式語言。事實上,為了避免訪問這些固有的特性,您在編碼中必須嚴格約束(只是使用 EL、標籤庫和類似的特性)。它基本上是一種在很大程度上開放的訪問模型。

拿 Velocity 與之比較。作為一種完全自包含的模板引擎和指令碼直譯器,Velocity 擁有完全封閉的模型。任何針對系統和/或 Java 程式語言的訪問都必須明確地啟用。預設情況,Velocity 模板中不能訪問 Java 程式語言的任何方面。這種封閉的模型使 Velocity 能夠提供分離的模板表示層,與任何應用程式業務邏輯或者資料管理程式碼清晰地劃分開。

現在讓我們把這種模板引擎與 Tomcat 5 的最新版本整合在一起,看一看 Velocity 在伺服器端的應用。

與 Tomcat 5 一起部署 Velocity
Velocity 發行包帶有一個org.apache.velocity.servletVelocityServlet庫類,擴充套件它可以很快地建立一個模板處理 servlet。作為獨立的客戶機應用程式測試的任何模板都可以使用VelocityServlet部署在伺服器上。把獨立的 Velocity 模板轉移到 Web 應用程式中相對比較簡單。只需要以下幾個步驟:

Velocity 中的工具
工具是在模板中可以通過 Velocity 上下文使用的實用 Java 物件。雖然可以手工把這些物件附加到上下文中,但VelocityViewServlet工具箱管理器可通過更加靈活和結構化的方式完成。在模板中一般是通過呼叫工具的方法來使用它們。VelocityViewServletVelocityStruts都提供了非常有價值的經過測試的工具,在作為一種檢視技術部署 Velocity 時極其有用。

  1. org.apache.velocity.servlet.VelocityServlet類派生一個 Servlet 類。
  2. 重寫並實現其中的一個handleRequest()方法。
  3. handleRequest()的實現中,新增希望在模板中作為上下文屬性使用的資料或工具(請參閱側欄Velocity 中的工具)。
  4. handleRequest()的實現中,從檔案或資源(如 JAR 檔案)中取得模板並返回它。

在示例程式碼包中,com.ibm.dvworks.velocity.VelTestServlet就是按照上述步驟建立的一個 servlet。您可以檢視webapps/vservlet/WEB-INF/src目錄下的程式碼。如果改變了這些程式碼,一定要使用 compile.bat 批處理檔案重新編譯它。

部署描述符(web.xml 檔案)定義了該 servlet 並把它對映到/vServletURL 模式中,如清單 19 所示:

清單 19. 自定義基於 Velocity 的 servlet 的 Tomcat 部署描述符

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
 <servlet>
    <servlet-name>vServlet</servlet-name>
    <servlet-class>com.ibm.dvworks.velocity.VelTestServlet</servlet-class>
 </servlet>

 <servlet-mapping>
    <servlet-name>vServlet</servlet-name>
    <url-pattern>/vServlet</url-pattern>
  </servlet-mapping>
</web-app>

載入並處理的模板放在webapps/vServlet目錄中。在這個例子中,模板檔案稱為variables.vm。測試之前一定要保證velocity-dep---?.jar檔案已經放在webapps/vServlet/WEB-INF/lib目錄下,然後啟動 Tomcat 5 並訪問http://localhost:8080/vservlet/ServletURL。

部署 VelocityViewServlet
要把模板功能擴充套件到 Web 應用程式中,應該使用 Velocity 工具集中的VelocityViewServlet。Velocity 工具是 Velocity 的一個子專案(請參閱參考資料找到這個 URL 並下載最新的版本)。該 Servlet 為 Velocity 用作一種檢視層技術提供了更復雜的支援,既可以與 JSP 技術聯合使用也可以代替後者。使用VelocityViewServlet可以減少許多冗餘程式碼,因為它提供了:

  • 對請求物件和屬性、會話物件和屬性以及 servlet 上下文和屬性的直接模板訪問
  • 正式的、可外部配置的“工具箱”,可以增加在模板中使用的自定義工具(這裡講的工具只是具有公共方法的已編譯的類)
  • 一個通用的、經過測試的、隨時可用的工具庫

要把VelocityViewServlet整合到 Web 應用程式中,可以看一看示例 velview Web 應用程式(在webapps/velview目錄中)。該應用程式包括本文中所討論的那些模板。此外,它還顯示了請求、會話以及 servlet 上下文物件的屬性。整合的步驟如下:

首先要保證velocity-tools-view.jar檔案在應用程式的lib目錄中。當然,這個 velocity JAR 檔案也應該在那兒。

在部署描述符 web.xml 檔案中,包括VelocityViewServlet。初始化引數是一個工具箱描述 XML 檔案。該 servlet 對映為處理所有副檔名為 .vm 的檔案,如清單 20 所示:

清單 20. VelocityViewServlet 的 Tomcat 部署描述符(web.xml)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <servlet>
    <servlet-name>velocityView</servlet-name>
    <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
    <init-param>
      <param-name>org.apache.velocity.toolbox</param-name>
      <param-value>/WEB-INF/toolbox.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>velocityView</servlet-name>
    <url-pattern>*.vm</url-pattern>
  </servlet-mapping>
</web-app>

該例子的工具箱描述符(toolbox.xml)檔案中,包含了兩個來自 Velocity 工具庫的通用工具可以在模板DateToolMathTool中訪問。這兩個工具使我們能夠格式化日期和時間資訊,並在模板中執行浮點運算,如清單 21 所示:

清單 21. 包括 DateTool 和 MathTool 的工具箱描述符

<?xml version="1.0"?>
<toolbox>
  <tool>
   <key>date</key>
   <scope>application</scope>
   <class>org.apache.velocity.tools.generic.DateTool</class>
 </tool>
<tool>
  <key>math</key>
  <scope>application</scope>
  <class>org.apache.velocity.tools.generic.MathTool</class>
</tool>
...

VelocityViewServlet中有一組常用的標準工具,如表 1 所示:

工具名 描述
LinkTool 處理 URI。該工具經常會用到,如果在模板中建立可點選的連結就要用到該工具,可以生成依賴於上下文的 URI 部分。
CookieTool 使模板能夠建立或訪問瀏覽器緩衝的 cookie。
ParameterParser 簡化後面收到的請求引數的解析。

還有兩個高度專門化的、不那麼常用的工具,如表 2 所示:

工具名 描述
ViewRenderTool 使模板能夠解析包含 VTL 的字串。
AbstractSearchTool 提供了一種骨架工具(必須使用自定義的 Java 程式碼來擴充套件),以便實現線上搜尋和搜尋結果分頁。

您可以使用http://localhost:8080/velview/variables.vmURL 測試 velview 應用程式。您應該開啟模板原始碼看一看所用的 Velocity 引擎、LinkToolCookieTool

與 Struts 框架的互操作
Struts 是一種構造基於 MVC 模型的框架的流行 Web 應用程式。Struts 預設的檢視元件技術是 JSP 技術。但是,可以很容易把 Velocity 整合進來作為檢視元件。圖 1 說明了 Velocity 的這種具體應用:

重要的是要看到,在這種結合中 Velocity 並沒有代替 JSP 技術。相反,JSP 技術和 Velocity 模板可以協同工作。整合 Velocity 需要配置VelocityViewServlet以便處理 .vm 模板,就像部署 VelocityViewServlet部分所講的那樣。這意味著.jsp 檔案將繼續由容器(即 Tomcat 5 中的 Jasper)處理,而任何 .vm 模板則傳遞給 Velocity。

Velocity Tools 子專案中的VelocityStruts元件(請參閱參考資料)包含整合 Velocity 與 Struts 的所有功能。VelocityStruts提供了一組專用的 Velocity 工具,用於訪問 Struts 專有的資源和 Velocity 模板中的資訊。表 3 列出了最常用的工具:

工具名 描述
StrutsLinkTool 針對 Struts 的 LinkTool 專用版本,提供了setAction()setForward()訪問預先配置的活動對映。
FormTool 訪問 Struts 的表單 beans。
ErrorsTool 處理 Struts 錯誤訊息,包括對國際化的支援。
MessageTool 提供對 Struts 國際化支援的訪問,尤為特別的是依賴於語言的訊息資源。

還有一組工具專用於 Struts 1.1 中的新特性,如表 4 所示:

工具名 描述
SecureLinkTool 用於 Struts 1.1 的安全連結(SSL)擴充套件。
ActionMessagesTool 提供對 Struts 1.1 新物件ActionMessages的訪問。
TilesTool 提供對 Struts 1.1 Tiles 擴充套件支援的訪問。
ValidatorTool 提供對 Struts 1.1 Validator 擴充套件的訪問,生成程式碼驗證表單輸入欄位。

webapps/struts-example目錄中可以找到一個例子,使用 Struts 而非 JSP 技術建立 Struts 頁面。本例中我們使用 Struts 取代了例項 Web 應用程式所釋出的第一個標題頁,您可以試著改變其他的頁面。下面列出了操作的步驟。

  1. 把 Velocity 庫複製到 Struts 示例應用程式下的WEB-INF/lib目錄中。要使用 Tomcat 5(5.0.16 是撰寫本文時的最新版本)和 Struts 1.1,需要把以下 JAR 檔案複製到webapps/struts-example/WEB-INF/lib目錄中:
    • velocity-tools-1.1-beta1.jar
    • velocity-1.4-rc1.jar

  2. 然後在 Struts 配置檔案(WEB-INF/struts-config.xml),把 Struts 動作對映設定為轉向 index.vm 檔案而不是 index.jsp 檔案,如清單 22 所示:
    清單 22. 把 Struts 動作轉向 index.vm
    
        <action    path="/logoff"
                   type="org.apache.struts.webapp.example.LogoffAction">
          <forward name="success"              path="/index.vm"/>
        </action>
    

  3. 在部署描述符WEB-INF/web.xml檔案中配置VelocityViewServlet處理.vm檔案。同樣把歡迎檔案設為 index.vm 而非 index.jsp,如清單 23 所示:
    清單 23. 改變 struts 示例 Web 應用程式的部署描述符
    
    <!-- Action Servlet Configuration -->
      <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
          <param-name>config</param-name>
          <param-value>/WEB-INF/struts-config.xml, 
          /WEB-INF/struts-config-registration.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
    
    
    
     <servlet>
        <servlet-name>velocity</servlet-name>
        <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet
        </servlet-class>
        <init-param>
          <param-name>org.apache.velocity.toolbox</param-name>
          <param-value>/WEB-INF/toolbox.xml</param-value>
       </init-param>
        <init-param>
          <param-name>org.apache.velocity.properties</param-name>
          <param-value>/WEB-INF/velocity.properties</param-value>
       </init-param>
      </servlet>
    
    
      <!-- Action Servlet Mapping -->
      <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    
      <servlet-mapping>
        <servlet-name>velocity</servlet-name>
        <url-pattern>*.vm</url-pattern>
      </servlet-mapping>
    
    
      <!-- The Welcome File List -->
      <welcome-file-list>
        <welcome-file>index.vm</welcome-file>
      </welcome-file-list>
    

  4. 最後,把 toolbox.xml 和 velocity.properties 檔案從本文的原始碼下載中(請參閱參考資料)移動到WEB-INF目錄下。

新的 index.vm 檔案如清單 24 所示,可以把它與原來的 index.jsp 檔案比較。

清單 24. 通過使用 index.vm Velocity 模板與 Struts 互操作

<html>
<head>
<title>$msg.get("index.title")</title>
</head>
<body bgcolor="white">

#if ( !$application.database)
  <font color="red">
    ERROR:  User database not loaded -- check servlet container logs
    for error messages.
  </font>
  <hr>
#end

<h3>$msg.get("index.heading")</h3>
<ul>
<li>
<a href="$link.setURI("editRegistration.do").addQueryData("action","Create")">
$msg.get("index.registration")
</a>
</li>
<li>
<a href="$link.setURI("logon.jsp")">
$msg.get("index.logon")
</a>
</li>
</ul>

<p>&nbsp;</p>
<a href="$link.setURI("tour.do")">
<font size="-1">$msg.get("index.tour")</font>
</a>
<p>&nbsp;</p>
<img src="$link.setURI("powered-by-logo.gif")" alt="Powered by Velocity"/>
</body>
</html>

在 index.vm 中,整個模板都使用$msg內的訊息工具訪問 Struts 的地域有關的國際化資源。通過對包含國際化字串的資源包的本地化更改,這種方法避免了模板中的多數硬編碼字串。

您可以使用 VTL 的條件指示符#if直接檢查在 servlet 上下文中是否存在資料庫屬性。$application引用可用於訪問 servlet 上下文中的任何屬性($request$response$session也可用於訪問其他 Servlet API 物件的屬性)。

LinkToolsetURI()方法用於生成伺服器端到 Struts 動作和“Powered by Velocity”標誌圖片的 URI 連結。注意,這裡使用LinkTooladdQueryData()方法向結果 URI 種增加附加的動作資訊。

要測試該 Velocity 頁面,您可以啟動 Tomcat 5 並訪問http://localhost:8080/struts-example/URL。注意它的結果與原來的 JSP 版本完全一致。

結束語
Velocity 模板處理程式可以直接整合到 Java 語言應用程式中,立即提供報告生成或者模板處理的功能。

將模板引擎擴充套件到 Web 應用程式,可以使用VelocityServlet處理動態生成 HTML 輸出的 Velocity 模板。Velocity 工具專案對使用VelocityViewServlet元件化 Web 層應用程式開發提供了更多的支援。VelocityViewServlet以模板為基礎為基於 Web 的 UI 構造提供了方便的檢視層。

在使用 MVC 模型框架設計複雜的 Web 應用程式時,Velocity 作為一種檢視/模板化技術——以VelocityViewServlet的形式——可以很方便地插入到框架中。對於流行的 Jakarta Struts MVC 框架,Velocity 可以與基於 JSP 的檢視技術協作,也可以和選擇的任何模型技術進行互動。

參考資料

  • 您可以參閱本文在 developerWorks 全球站點上的英文原文.

  • 下載本文的原始碼

  • 關於 Velocity 模板引擎的最新版本、相關文件、郵件列表和社群新聞,請訪問Velocity 官方站點

  • 可以從Velocity 工具子專案下載VelocityViewservlet、與 Struts 整合的VelocityStruts以及其他一些工具。

  • Struts MVC 框架是一種非常流行的開放原始碼的用於建立 Web 應用程式的框架。Velocity 可以與 JSP 技術一起作為 Struts 的檢視技術。

  • Turbine是一種從頭開始設計的開放原始碼 MVC 框架,可以很好地使用 Velocity 作為檢視元件技術。

  • 看看 Srikanth Shenoy 和 Nithin Mallya 的文章,瞭解如何整合 Struts、Tiles 和 Java Server FacesdeveloperWorks,2003 年 9 月)。

  • 要使用 WebSphere Studio V5 建立基於 Struts 的 Web 應用程式,請閱讀 Colin Yu 和 Jane Fung 的系列文章(2003 年 11 月)。