使用 Velocity 實現客戶端和服務器端模板

 
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 並使用它打印 $treeList ArrayList ―― 如果這個列表已經定義的話。調用的語法很簡單,即 #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 並把它映射到 /vServlet URL 模式中,如清單 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/Servlet URL。

部署 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 所示:

表 1. VelocityViewServlet 中的標準工具

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

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

表 2. 專門的 VelocityViewServlet 工具

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

您可以使用 http://localhost:8080/velview/variables.vm URL 測試 velview 應用程序。您應該打開模板源代碼看一看所用的 Velocity 引擎、 LinkToolCookieTool





回頁首


與 Struts 框架的互操作

Struts 是一種構造基於 MVC 模型的框架的流行 Web 應用程序。Struts 默認的視圖組件技術是 JSP 技術。但是,可以很容易把 Velocity 集成進來作爲視圖組件。圖 1 說明了 Velocity 的這種具體應用:


圖 1. Velocity 與 Struts MVC 框架集成

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

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

表 3. 用於 VelocityStruts 集成的工具

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

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

表 4. 專用的 Struts 1.1 訪問工具

工具名 描述
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 的視圖技術協作,也可以和選擇的任何模型技術進行交互。

發佈了14 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章