簡介:本教程是系列教程(共六部分)的最後一部分,向您展示了如何利用 Spring 框架來使用 Java™Server Page(JSP)、Velocity、Tile 和 PDF 導出功能。您將用 Model-View-Controller (MVC)中的 V 做試驗,即 Spring MVC 中內置的各種 Web 視圖。通過對 Spring MVC 所支持的各種視 圖技術的全面介紹,您將看到在整個系列教程中構建的樣例電話本應用程序中實現這些技術有多麼輕松。
開始之前
本系列教程適合於需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 應用服務器上使用 Spring 框架的強大功能的 Java Platform、Enterprise Edition(Java EE)開發人員。
關於本系列教程
本系列教程共分為 6 個部分,向您介紹了 Spring 框架及 Spring 框架怎樣與 Geronimo 結合使用。 我們將從檢驗各種 Spring 框架方法及其怎樣與 Geronimo 服務器結合使用入手。在整個系列教程中,您 將開發和部署個人電話本應用程序。該應用程序包括以下功能:
顯示電話本
顯示每個條目的詳細信息
向電話本中添加一個新條目
編輯、修改和刪除條目
向條目中添加更多詳細信息,例如主電子郵件地址
第 1 部分 介紹了 Spring 框架的各個模塊,並介紹了每個模塊與在 Geronimo 應用服務器上開發 Java EE 應用程序的關系。該部分還說明了 Spring 框架所基於的方法。
第 2 部分 介紹了如何使用 Spring 框架在 Geronimo 上構建第一個骨架系統應用程序。
第 3 部分 中,您將采用通過 Apache Derby 數據庫添加 Java 數據庫連接 (JDBC) 支持來擴展在第 2 部分中獲得的 Geronimo 應用程序。您還將了解如何將對象關系映射 (ORM) 集成到使用 iBatis 的應 用程序中。
第 4 部分 中,您將面對 Spring AOP 和 Spring Web 框架。使用 Spring AOP,任何受 Spring 框架 管理的對象都可變為面向方面的,並且本教程利用了通過 Spring AOP 提供的聲明式事務管理服務。
第 5 部分 介紹了 Spring MVC。該教程向您介紹了 MVC 框架及 Web 視圖,使您可以了解 Spring MVC 的入門知識。
在最後一部分,也就是第 6 部分中,介紹了如何通過 Spring 框架使用 JSP、Velocity、Tile 和 PDF 導出功能。您將使用和體驗 Spring MVC 內置的各種 Web 視圖。
關於本教程
如上所述,本系列教程的第 5 部分提供了對 Spring MVC 模塊的完整介紹。您了解了 Spring MVC 所 提供的各種控制器 —— MVC 中的 C。本教程將檢驗各種視圖技術 —— MVC 中的 V。
Web 應用程序開發人員所面臨的最大挑戰之一是創建可適應的設計。使視圖組件具有靈活性非常具有 挑戰性。由於 Spring 對視圖的支持十分健壯,因此這種挑戰較易管理。本教程中使用 JSP、Tile、 Velocity 和 PDF 導出功能的目的在於演示 Spring MVC API 如何使這一切成為可能。
JSP 和 Velocity 是兩項互補的視圖技術。您可以用任意一項技術來創建視圖,每項技術都有自己的 優缺點。本教程演示了在樣例電話本應用程序中用其中一項技術替代另一項技術有多麼簡單。
首先我們將查看 Spring MVC 中的 JSP 支持,然後查看 Tile,一種用於視圖布局管理的優秀模板引 擎。Tile 使您可以輕松地管理 Web 頁面布局,而且 Spring 有對 Tile 的內置支持。您將使用這些類來 更改電話本應用程序的 Web 布局。
接下來,您將通過使用 Velocity 模板引擎定義的視圖來替代對 JSP 的使用。Velocity 使您可以輕 松地訪問視圖中的 Java 對象,而無需復雜的定義和 Java 結構,例如 Try Catch 循環。
最後,您將看到如何調整電話本應用程序的主頁使其顯示為 PDF 文件。Spring 將處理創建 PDF 時所 需的全部麻煩而復雜的代碼和邏輯。它為您提供了一種干淨的 API 來處理內容,而不必為 PDF 的細節而 困惑。
先決條件
要繼續學習本教程,您應當對以下內容有基本了解:
面向對象的編程
Java Platform,Enterprise Edition(Java EE)的術語
SQL 語句
XML 語義
JSP 標記、標記庫和標記庫描述符
了解 MVC 更佳,並且具有 Velocity 的工作知識也會非常有利,但並非強制要求。
系統要求
您的系統需要至少滿足以下要求才能繼續學習本系列教程:
The Spring Framework v1.2.8 —— 具有所有依賴性的壓縮文件。
Apache Geronimo 1.1 —— Geronimo 是 Apache 的 Java 2 Platform, Enterprise Edition(J2EE )認證應用服務器。
Apache Derby 數據庫 —— 本教程使用 Derby,該數據庫是開源的輕量級數據庫。Derby 是嵌入到 Geronimo 1.1 裡的,因此不需要再單獨安裝。
Velocity JAR 文件 —— 您將需要來自 Velocity 模板引擎的 JAR 文件。您既可以從 Spring 框架 安裝中復制,也可以從 Velocity 站點下載。
iText —— 這是用於快速生成 PDF 的 PDF 庫。Spring 將使用此庫來生成 PDF,並且是 Spring 框 架的一部分。
Struts —— 這是來自 Spring 的 Tile 支持,它依賴於 Struts API。您需要使用來自此 API 的主 struts.jar 文件,可以在 Spring 框架中找到。
Spring Tile 支持所需的常見庫 JAR 文件 —— 您需要使用 commons-digester.jar、commons- collection.jar 和 commons-beanutils.jar。這些都來自 Spring 框架安裝,因此只需將這些文件復制 到開發環境中。
Standard taglib API —— 由於在 JSP 中使用的是 JSP 標准標記庫(JSP Standard Tag Library, JSTL)標記,因此需要使用此壓縮文件中的 JAR 文件。
標准的 JSTL 庫 —— 當前版本為 1.1.2。
Apache Ant —— 確保正確配置 Ant 並且其 /bin 目錄位於 Path 系統變量中。
Java 1.4.2 —— 確保 Java 安裝並運行在系統中。
安裝和配置軟件
此部分包含安裝和配置開發、部署和運行示例應用程序所必需的軟件的說明。
安裝 Spring 框架和 Geronimo:要使樣例代碼運行,需要安裝運行 Geronimo 和 Spring 框架。(有 關安裝指南,請參閱本系列教程的 第 2 部分。)
解析 Tile 依賴性所需的 JAR 文件:Spring 中的 Tile 支持需要使用以下 JAR 文件:
Struts.jar —— <SPRING_HOME>\lib\struts
Commons-digester.jar —— <SPRING_HOME>\lib\jakarta-commons
Commons-collection-3.2.jar —— <SPRING_HOME>\lib\jakarta-commons
Commons-beanutils.jar —— <SPRING_HOME>\lib\jakarta-commons
所有這些 JAR 文件都隨 Spring 框架安裝包附帶而來。您可以在以上列表中指出的目錄中找到它們, .jar 文件名旁邊。確保將所有這些 JAR 文件復制到 <WORKSPACE>/phonebook/lib 目錄中。
解析 Velocity 依賴性所需的 JAR 文件:樣例電話本應用程序需要包括 velocity-1.4.jar 才能使用 Velocity。Velocity 還需要使用復制的 commons-collections.jar 才能確保 Tile 完整性。Velocity .jar file 可以在 Spring 安裝目錄的 <SPRING_HOME>\lib\velocity 文件夾中找到。確保將此文 件復制到 <WORKSPACE>/phonebook/lib 目錄中。
解析 PDF 依賴性所需的 JAR 文件:Spring 的 PDF 支持 API 將使用 iText PDF 庫,因此需要該庫 為應用程序創建 PDF。同其他 JAR 文件一樣,此文件也是預打包在 Spring 安裝中。您可以在 <SPRING_HOME>\lib\itext 中找到 itext-1.3.jar 文件。將此文件也復制到 lib 目錄中。
安裝 Apache 的 Standard Taglib 和 Spring taglib:您將使用在本系列教程的 第 5 部分 中定義 的 JSP 並且使用它們擴展應用程序,因此需要安裝 JSTL 庫。(請參閱本系列教程的第 5 部分中的安裝 說明。)
應用程序的數據模型定義和數據庫設置:您將使用在本系列教程的其它部分中創建的同一個 Derby 數 據庫。數據模型也是相同的。如果在第 3 部分、第 4 部分或第 5 部分中創建了數據庫和表,則應當已 經設置好了一切。如果未創建,請按照 第 3 部分 中的說明先完成設置。
Spring 所支持的各種視圖技術簡介
Spring 框架證明其威力的領域之一在於將視圖技術與其余 MVC 框架及其功能分離,從而使開發人員 可以非常迅速地更改視圖技術。例如,如果希望使用 Velocity 替代 JSP,則調整一些配置文件是十分簡 單的事。在此部分中,您將看到 Geronimo 應用程序怎樣可以從結合使用這些技術與 Spring 框架中獲益 。
JSP 支持
第 5 部分 演示了 Spring Framework 對 JSP 的擴展支持。Spring 提供了可以使用或擴展的開箱即 用的控制器,這取決於應用程序的需求。Spring MVC 使您可以輕松地將精力集中在內容上,而無需擔心 實現和整合的詳細信息。第 5 部分為您提供了大量示例。為 Web 頁面中的操作定義了控制器,然後這些 控制器將根據那些操作返回 JSP 視圖。
在 Geronimo 應用程序中使用 JSP 而不使用 servlet 使您可以輕松地管理 Web 應用程序的內容。
Tile
Tile 是內置在 Apache Struts 框架中的模板引擎。Tile 提供的一些重要功能包括:
能夠通過組合 Tile 創建屏幕/視圖:頁眉、頁腳、菜單、主體等等。
可以將定義集中在 XML 文件中或直接集中到 JSP 頁面。此示例使用了一個集中的 XML 文件用於視圖 定義。
能夠繼承定義和擴展另一個定義或覆蓋參數。
能夠使用 Tile 作為布局管理器甚至還可以為各種應用程序重用布局。
能夠使用 Tile API 輕松地創建國際化的內容。
Spring MVC 提供了一個簡單的框架用於將 Tile 集成到 Web 應用程序中。Tile 最重要的用途是作為 Web 布局管理器。Tile 簡化了視圖布局的更改過程,您無需更改任何 JSP 頁面。本教程中的示例應用程 序向您展示了如何定義布局並在各種頁面中使用它。
如果 Geronimo 應用程序的視圖在多個頁面中都使用類似的視圖組件,則使用 Tile 還能讓您受益。 例如,如果所有頁面必須具有相同的頁眉和頁腳,甚至是具有相同的左鍵菜單,則可以將這些內容定義為 Tile 並且使用該 Tile API 作為布局管理器。
Velocity
Velocity,另一種基於 Java 技術的模板引擎,允許在視圖中引用 Java 代碼定義的對象。Velocity 的主要目標之一是提供一種更簡單的視圖技術作為 JSP 視圖技術的備用方法,方法是嘗試消除 JSP 標記 和 Java 構造函數(如 Try Catch 循環)的復雜應用。
Velocity 還允許 Web 設計人員在 MVC 模型的基礎之上與應用程序開發人員並行工作。Web 頁面設計 人員可以將全部精力集中在創建站點上,而編程人員可以將精力集中在應用程序代碼上,因為 Velocity 將 Java 代碼與 Web 頁面分隔開來,使頁面更具有可維護性。
圖 1 顯示了使用 Velocity 作為視圖技術的基本 MVC 模型。
圖 1. 提供了 MVC 中的 V 的 Velocity
視圖被創建為 Velocity 模板或 .vm 文件。您可以將 Java 對象作為對這些視圖的引用來傳遞。本教 程的稍後部分中將介紹其工作方式。
干淨可維護的 JSP 是使用 Velocity 作為 Geronimo 應用程序的視圖技術的主要優點。Velocity 視 圖包含表示 Web 頁面視圖的 HTML,同時對 Java 對象的引用最少。
PDF 導出功能
為 Web 視圖生成 PDF 對 Web 應用程序開發人員來說始終是個挑戰。僅創建 PDF 就需要完成一系列 繁瑣的操作,而這甚至還沒有考慮到添加內容時將遇到的困難。Spring MVC 的 PDF 支持很大程度上簡化 了該過程。它會處理創建 PDF 所涉及的所有復雜步驟,還提供了一個流線化的 API,以便將內容添加到 其中。
Spring 的 PDF 支持使您可以輕松地在 Geronimo 應用程序中將頁面視圖導出為 PDF。
現在您已經打好了本教程涉及的各種技術的基礎,是時候將它們集成到電話本應用程序中了。
擴展電話本應用程序來使用各種視圖技術
在此部分中,首先概述如何將先幾節中的視圖技術集成到示例電話本應用程序中。隨後討論實際實現 。
將各種視圖技術集成到電話本應用程序中
集成 Tile 包括以下步驟:
為應用程序定義一種布局機制。這是一個簡單的 Web 應用程序,因此是頁眉 > 主體 > 頁腳。
定義 Tile 模板文件。
為 Tile 模板定義視圖解析程序。
將此模式擴展到電話本應用程序的所有頁面。
集成 Velocity 包括以下步驟:
定義一個新控制器來處理 Velocity 模板請求。
為主頁創建一個 Velocity 模板。
為 .vm 文件定義一個視圖解析程序。
生成 PDF 包括以下步驟:
定義一個控制器來處理 PDF 視圖請求。
定義一個視圖類來為電話本主頁創建 PDF。
將此添加到 Application Context 的視圖解析程序鏈中。
工作區的目錄結構
圖 2 顯示了應用程序的布局方法。按慣例下載本教程附帶的源壓縮文件(請參閱 下載 部分)並將其 解壓縮。
圖 2. 解壓縮源文件後的應用程序目錄結構
如您在 圖 2 中列出的目錄結構中所見,沒有用於 Tile 的新控制器 —— 一切全在於配置。在下面 的部分中,您將看到 Spring 怎樣使這一切成為可能。
學習目標
如果您到目前為止一直在閱讀本系列教程,則應當一直在創建電話本應用程序。每期文章中都添加了 更多功能。迄今為止,應用程序允許您:
顯示電話本條目(默認情況下是主頁)。
向電話本中添加條目。
刪除選定條目。
修改選定電話本條目。
首先,您將把現有電話本應用程序轉換為使用 Tile 作為布局管理器。首先將在電話本模板 JSP 中定 義 Web 頁面布局。Tile 視圖定義文件將把特定視圖的定義包含在此電話本模板中。
圖 3 為您提供了一種使用 Tile 定義布局的方法。
圖 3. 解壓縮源文件後的應用程序目錄結構
如您所見,Tile 用作布局管理器並且單獨的實現頁面都被視為 JSP Tile。布局管理器可以以任何一 種您希望的方式替換 Tile,只要通過定義為模板元素提供實現頁面。
接下來,看看如何僅通過更改 Application Context 文件中的某些配置來用 Velocity 中定義的視圖 替換 JSP。對於本教程,您將更改將被 Velocity 模板替換掉的主頁。您可以使用它作為將其余頁面轉換 為 Velocity 視圖的練習。
應用程序的最後一部分向您展示了如何使用 Spring MVC 的 AbstractPdfView 類來創建 PDF 視圖。 此類將為您處理所有創建 PDF 文檔的工作,並為您提供一個干淨的文檔用於添加內容。然後將手動創建 電話本主頁並以 PDF 形式返回。
注:Spring MVC 的 PDF 支持不同於 Web 頁面的屏幕捕捉程序;還有其他工具可用於該目的。您必須 手動創建需要導出到 PDF 文檔中的視圖。
將 Tile 集成到應用程序中
到目前為止,您的電話本應用程序一直是使用 JSP 開發的。查看 Tile 與應用程序協同工作的最佳方 法是使用 Tile 作為布局管理器。下面幾部分向您展示了該方法。
將 Tile 標記庫添加到應用程序中
正如任何其他 JSP 標記庫一樣,必須將 Tile 庫添加到 Web 應用程序部署描述符中,然後才能使用 它。只需將清單 1 中的 taglib 元素添加到 web.xml 文件中。
清單 1. 在 web.xml 中添加 Tile 標記庫
<jsp-config>
<taglib>
<taglib-uri>/tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
</jsp-config>
現在需要定義 Web 應用程序的布局。
在 Phonebook-layout.jsp 中定義一個模板布局
在 Tile 定義的上下文中,模板是使用 JSP 自定義標記庫來描述頁面布局的 JSP 頁面。它定義了在 沒有指定內容的情況下應用程序頁面的外觀。內容將在運行時插入。正如您所見,它是一個簡單的模板; 將默認頁面結構定義為頁眉 > 主體 > 頁腳。清單 2 顯示了此 JSP 的代碼。
清單 2. phonebook-layout.jsp 顯示 Web 頁面的布局
<%@ taglib prefix="tiles" uri="/tiles" %>
<!-- This page defines the general layout for Phonebook Application Pages -->
<tiles:insert name="header"/>
<tiles:insert name="body"/>
<tiles:insert name="footer"/>
您將為所有頁面應用此模板。它將使用 tiles:insert 標記來插入按名稱屬性表示的內容。正如此處 所示,沒有內容,只有頁面的布局。
現在需要把視圖與 Tile 視圖名稱關聯起來,這是接下來您要做的工作。
配置 Tile 使其與 Spring 協同工作
為了能夠將 Tile 與 Spring MVC 結合使用,必須使用包含在視圖定義中的文件對其進行配置。在 Spring 中,使用 TilesConfigurer 類來完成此操作。清單 3 顯示了它是如何在 Application Context 文件中定義的。
清單 3. 視圖定義文件名被傳遞給 Application Context 中的 TilesConfigurer
<!-View Resolver for Tiles -->
<bean id="tilesViewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="1" />
<property name="basename" value="views-phonebook-tiles"/>
</bean>
<!-- Tiles configurer reads the view definition from the XML file -- >
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
<property name="factoryClass"
value="org.apache.struts.tiles.xmlDefinition.I18nFactorySet"/>
<property name="definitions">
<list>
<value>/WEB-INF/defs/phonebook-definitions.xml</value>
</list>
</property>
</bean>
您可以看到視圖定義文件的名稱為 phonebook-definitions.xml。TilesConfigurer 將在初始化時讀 取此文件,然後使可用的定義文件中的所有視圖都可在應用程序中使用。
另一個 bean 定義是用於 ViewResolver 的,Spring 必須使用它才能解析視圖。 ResourceBundleViewResolver 是 Spring 框架中最常用的視圖解析程序之一。您需要傳遞到定義映射到 類和 URL 的視圖名的屬性文件中。清單 4 顯示了 views-phonebook-tiles.properties 文件中的內容。
清單 4. views-phonebook-tiles.properties 文件中的視圖定義
home-mvc.class=org.springframework.web.servlet.view.tiles.TilesView
home-mvc.url=home-mvc
addentry-mvc.class=org.springframework.web.servlet.view.tiles.TilesView
addentry-mvc.url=addentry-mvc
modifyentry-mvc.class=org.springframework.web.servlet.view.tiles.TilesView
modifyentry-mvc.url=modifyentry-mvc
清單 4 中的定義告訴 Spring MVC 模塊應用程序中有三個視圖,並且每一個視圖都是一個 TilesView 。第二行顯示特定於表示這些視圖的每個頁面的 URL。
接下來,您將看到如何將由特定 JSP 實現的實際視圖插入到 Tile 布局中。
將視圖與實際 JSP 實現關聯起來
清單 2 中所示的布局 Tile 是通用的並將被所有 Web 頁面使用。它不知道關於主頁、addEntry 或 ModifyEntry JSP 頁面內容的任何信息。就是這樣設計的,因為它允許您對很多頁面重用此布局。不是將 內容硬編碼到布局定義頁面中,而是在運行時將其作為參數傳遞到布局頁面中。
在 Spring 中,通過定義 XML 定義文件來完成這項工作。清單 5 顯示了在電話本應用程序中這是怎 樣完成的。
清單 5. 用於在運行時將內容傳遞到布局頁面的定義文件
<tiles-definitions>
<!-- DEFAULT MAIN TEMPLATE -->
<definition name="template"
page="/WEB-INF/jsp/phonebook-layout.jsp">
<put name="header"
value="/WEB-INF/jsp/header.jsp"/>
<put name="footer"
value="/WEB-INF/jsp/footer.jsp"/>
</definition>
<!-- The Home Page -->
<definition name="home-mvc" extends="template">
<put name="body" value="/WEB-INF/jsp/home-mvc.jsp"
type="page"/>
</definition>
<!-- The Add Entry Page -->
<definition name="addentry-mvc" extends="template">
<put name="body" value="/WEB-INF/jsp/addentry-mvc.jsp"
type="page"/>
</definition>
<!-- The Modify Entry Page -->
<definition name="modifyentry-mvc" extends="template">
<put name="body" value="/WEB-INF/jsp/modifyentry-mvc.jsp"
type="page"/>
</definition>
</tiles-definitions>
第一個定義通過為模板提供名稱和包含該模板內容的頁面來定義默認模板。如果返回至 清單 2(在 phonebook-layout.jsp 中為應用程序定義布局的位置),則會注意到插入到該布局中的視圖的名稱與在 此模板中定義的那些視圖名稱相同。因此本質上是將內容與這裡的這些定義中的模板關聯了起來。
模板定義中的 put 元素將告訴 Tile 框架獲取視圖的位置。在本例中,頁眉視圖將從 header.jsp 中 獲取其內容,而頁腳將從 footer.jsp 中獲取其內容。
下一個 Tile 定義將定義主頁。它將擴展默認模板並簡單地將主體插入 home-mvc.jsp 頁面中。如果 仔細查看此定義,您將看到如何構建主頁。以下是詳細操作過程:
由於 home-mvc 擴展了默認模板,因此它將自動從電話本布局中獲取頁眉和頁腳內容。
此定義將通過指向 home-mvc.jsp 頁面把主體置入主頁中。
Spring MVC 框架在運行時將把所有 JSP 收集到一起並將內容作為一個單頁面傳遞回給客戶機。這是 一種干淨而簡單的方法來編寫 Web 應用程序。
構建並運行它
查看運行的最簡單且最快速的方法是使用 Geronimo Web 控制台部署 phonebook.war 文件。您可以按 照 第 2 部分 中的說明部署 .war 文件。部署了 .war 文件後,請將浏覽器指向 http://localhost:8080/phonebook/home-mvc.act 以查看運行中的啟用了 Tile 的應用程序。圖 4 顯示 了執行它之後主頁的外觀。
圖 4. 浏覽器中啟用了 Tile 的主頁
現在您已經看到了 Tile 與 Spring 框架在 Geronimo 應用服務器上協同工作,您可以繼續將 Velocity 集成到應用程序中。
將 Velocity 集成到應用程序中
此部分將展示如何將 JSP 替換為使用 Velocity 定義的視圖 —— 也稱為 Velocity 視圖或 .vm 文 件。您將了解到通過僅對配置文件做出更改就可以更改視圖技術是多麼簡單。Web 應用程序必須包括 velocity.x.jar 文件才能使用 Velocity (請參閱 先決條件),因此請確保已經將該 .jar 文件復制到 了 WEB-INF/lib 目錄中。
將 Velocity 配置到 Spring 的 Application Context 中
有兩種方法可以讓 ApplicationContext 知道 Velocity 模板的存在。第一種方法是使用 VelocityConfigurer。您需要做的工作就是在 resourceLoaderPath 中提供 Velocity 模板的位置, VelocityConfigurer 將在運行時使來自該位置的所有模板都可用於應用程序。
另一種方法(且為首選方法)是定義自己的控制器,然後使用該控制器來定位視圖。清單 6 顯示了 phonebook-servlet.xml 文件中的這些 bean 定義。
頭兩個 bean 定義定義了對 home.vel 頁面的請求應當由 PhonebookVelocityController 類來控制。 您將在以後的部分中定義此類。第三個 bean 定義使用了 VelocityViewResolver 來解析帶有 .vm 擴展 名的所有頁面請求。
清單 6. 讓上下文知道 Velocity 視圖的存在的 Bean 定義
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/*.do">phonebookController</prop>
<prop key="/*.htm">phonebookFlowController</prop>
<prop
key="/*.flow">phonebookFlowController</prop>
<prop
key="/addentry-mvc.act">addEntryFormController</prop>
<prop
key="/home-mvc.act">phonebookHomeController</prop>
<prop
key="/modifyentry-mvc.act">modifyEntryFormController</prop>
<prop
key="/deleteentry-mvc.act">deleteEntryFormController</prop>
<prop
key="/home.vel">phonebookVelocityController</prop>
<prop
key="/home.pdf">phonebookPDFController</prop>
</props>
</property>
</bean>
<bean id="phonebookVelocityController"
class="phonebook.velocity.PhonebookVelocityController"/>
<!-- View Resolver for Velocity -->
<bean id="velocityViewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"
>
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
</bean>
定義 Velocity 控制器類
為什麼要首先為 Velocity 定義一個控制器?記住您要用從數據庫中讀取的所有電話本條目填充主頁 ,並且從數據庫中讀取此列表並把它作為一個對象傳遞給 Velocity 視圖。Velocity 引擎將把此對象傳 遞給主頁模板,該模板隨後將使用 Velocity 模板語言標記迭代此對象。清單 7 顯示了 PhonebookVelocityController 的代碼。
清單 7. 從數據庫中讀取 PhonebookEntries 並傳遞給視圖的類
public class PhonebookVelocityController implements Controller {
/** Creates a new instance of PhVelocityController */
public PhonebookVelocityController() {
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException, Exception {
ModelAndView view = null;
if (request.getRequestURI().indexOf("home")!=-1) {
view = new ModelAndView("phonebook-template");
view.addObject("viewId", "home");
view.addObject("time", new Date() );
WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().
getServletContext());
IPhonebookDataProvider pb = (IPhonebookDataProvider)
ctx.getBean("phonebook");
List phonebookEntries = pb.getPhonebookEntries();
System.out.println("Velocity No. of Entries =
"+phonebookEntries.size());
view.addObject("phEntries", phonebookEntries);
}
return view;
}
}
您可以看到控制器使用 WebApplicationContext 來獲取 phonebook bean 並從數據庫中讀取電話本條 目。然後它將此作為一個對象添加到視圖中。您接下來將看到 Velocity 模板怎樣讀取它。
為主頁視圖創建一個 Velocity 模板
如同您為 Tile 視圖所做的工作一樣,需要在模板文件中定義默認布局。清單 8 顯示了 phonebook- template.vm。
清單 8. 定義 Web 應用程序布局的默認模板
#set ($viewName = ${viewId})
#set ($viewName = "${viewName}.vm") ## View name is passed in dynamically from controller.
#parse("header.vm")
#parse($viewName)
#parse("footer.vm")
您可以看到電話本布局是一樣的。Velocity 引擎在運行時讀取此模板,並且解析標記將呈現作為參數 傳遞進來的組件。viewName 是由 phonebookVelocityController 在運行時動態傳遞的。
清單 9 顯示了主頁的模板代碼。
清單 9. 基於 Velocity 的主頁的模板代碼
#set ($time1 = ${time})
#set ($phonebook = ${phEntries})
<html>
<script type="text/javascript">
function goToAddEntryPage() {
document.myForm.action="/phonebook/addentry.vel";
document.myForm.method="GET";
document.myForm.submit();
}
function setId(entryID, rowID) {
document.myForm.entryID.value = entryID;
document.myForm.rowID.value = rowID;
}
function noRowSelected() {
var entryID = document.myForm.entryID.value;
var rowID = document.myForm.rowID.value;
if (entryID == "" || rowID == "") {
return "true";
} else {
return "false";
}
}
function goToModifyEntryPage() {
if (noRowSelected() == "true") {
alert("Please select an Entry to Modify");
return;
}
document.myForm.action="/phonebook/modifyentry-mvc.act";
document.myForm.method="GET";
document.myForm.submit();
}
function deleteEntry() {
if (noRowSelected() == "true") {
alert("Please select an Entry to Delete");
return;
} else {
var retVal = confirm("Please click OK to confirm your deletion. Click
Cancel otherwise");
if (retVal != true) {
return;
}
}
document.myForm.action="/phonebook/deleteentry-mvc.act";
document.myForm.method="POST";
document.myForm.submit();
}
</script>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Phone Book - $time1 </title>
</head>
<body>
<!--The Heading of the Phone Book Page-->
<h1 align=center>Phone Book</h1>
<BR>
<BR>
<!-- The table containing phone book contents. -->
<TABLE border="1" width="100%">
<TH width="5%" align=center>Select</TH>
<TH width="25%" align=center>Name</TH>
<TH width="15%" align=center>Home Phone</TH>
<TH width="15%" align=center>Work Phone</TH>
<TH width="15%" align=center>Cell Phone</TH>
<TH width="25%" align=center>Email</TH>
#foreach( $pbEntry in $phonebook )
#set ($i = $velocityCount - 1)
<TR>
<TD align=center><input type=radio name="data_"$i
alt="Select to Delete" align="middle"></TD>
<TD align=center>$pbEntry.getFirstName()
$pbEntry.getLastName()</TD>
<TD align=center>$pbEntry.getHomeNumber()</TD>
<TD align=center>$pbEntry.getWorkNumber()</TD>
<TD align=center>$pbEntry.getCellNumber()</TD>
<TD align=center>$pbEntry.getEmail()</TD>
</TR>
#end
</TABLE>
<BR>
<BR>
<table align=center>
<!-- The row containing command buttons -->
<TR align=center>
<!--
<TD><input type=submit name="Add" value="Add an Entry"
onclick="javascript:goToAddEntryPage()"></TD>
<TD><input type=button name="Modify" value="Modify
Selected Entry"
onclick="javascript:goToModifyEntryPage()"></TD>
<TD><input type=button name="Delete" value="Delete
Selected Entry"
onclick="javascript:deleteEntry()"></TD>
-->
</TR>
</table>
</body>
<BR><BR><BR><BR><BR><BR>
</html>
這個 home.vm 模板定義使用了 Velocity 的模板語言(Velocity Template Language,VTL)。此模 板的第二行將獲取由 Velocity 控制器類傳入的 pbEntries 對象。然後用它迭代用電話本條目填充表的 列表。
注:本教程並不詳細介紹 VTL。
此過程中的最後一步是為 .vm 請求添加 URL 映射。
為 *.vm 請求添加 URL 映射
現在您應當很熟悉清單 10 中所示的 servlet 映射。
清單 10. *.vm 請求的 URL 映射
<servlet-mapping>
<servlet-name>phonebook</servlet-name>
<url-pattern>*.vel</url-pattern>
</servlet-mapping>
這就是您需要做的全部操作。現在讓我們來看看它的運行情況!
構建並部署它
像以前一樣,查看運行的最簡單且最快速的方法是使用 Geronimo Web 控制台來部署本教程附帶的示 例軟件包中的 phonebook.war。然後將浏覽器指向 http://localhost:8080/phonebook/home.vm 以查看 運行中的 Velocity 引擎。
如果一切運行正常,則您的浏覽器頁面將如圖 5 所示。
圖 5. 顯示運行中的 Velocity 引擎的主頁
下一個部分將向您展示使用 Spring MVC 的 PDF 支持將 Web 頁面的內容返回為一個 PDF 是多麼簡單 。
將文檔視圖導出為 PDF
HTML 頁面並不總是查看模型數據的最佳方法,因此 Spring 使您可以簡單地生成 PDF 文檔。此部分 將向您展示如何將一直使用的同一個模型呈現為一個 PDF 文檔並將其以正確的內容類型返回給客戶機。
首先必須定義一個控制器以從數據庫表中獲取電話本條目的列表並將其傳遞給 PDF 視圖類,從而開始 開發 PDF 控制器。
定義 PhonebookPDFController 類
此類將使用 Spring ApplicationContext 的 bean 定義讀取 phonebookEntries 類,然後將其傳遞給 PhonebookPdfView 類所表示的 PDF 視圖。清單 11 顯示了此控制器的代碼。
清單 11. PhonebookPDFController 類讀取電話本條目並將其傳遞給 PhonebookPdfView 類
public class PhonebookPDFController extends
MultiActionController {
private static final String PDF_VIEW = "phonebook_pdfView";
private String pdfView = PDF_VIEW;
/** Creates a new instance of PbPDFController */
public PhonebookPDFController() {
}
public void setPdfView(String view) {
this.pdfView = view;
}
/**
* Custom handler for phonebook PDF document.
* @param request current HTTP request
* @param response current HTTP response
* @return a ModelAndView to render the response
*/
public ModelAndView handlePdf(HttpServletRequest request,
HttpServletResponse response) throws ServletException, Exception {
WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().
getServletContext());
IPhonebookDataProvider pb = (IPhonebookDataProvider)
ctx.getBean("phonebook");
List phonebookEntries = pb.getPhonebookEntries();
return new ModelAndView(this.pdfView, "phonebook",
phonebookEntries);
}
}
下一個邏輯步驟是定義 View 類,該類將通過模型輸出實際生成 PDF。
定義 PDF View 類
PhonebookPdfView 是 AbstractPdfView 類的子類,用於實現特定於 Web 視圖的 PDF 文檔的自定義 生成。在此類中最值得注意的方法是 buildPdfDocument。這是所有魔術變幻的關鍵位置所在。清單 12 顯示了為電話本應用程序生成 PDF 的完整代碼。
清單 12. PDF 視圖類將創建 PDF 文檔
public class PhonebookPdfView extends AbstractPdfView {
private static final Font HEADLINE_FONT = new Font( Font.TIMES_ROMAN , 18,
Font.BOLD, Color.black );
private static final Font DATA_HEAD_FONT = new Font( Font.TIMES_ROMAN, 10,
Font.BOLD, Color.black );
private static final Font TEXT_FONT = new Font( Font.TIMES_ROMAN, 8,
Font.NORMAL,
Color.black );
private static final Font FOOTER_FONT = new Font( Font.TIMES_ROMAN, 5,
Font.NORMAL,
Color.black );
private static final int NO_OF_COLUMNS = 5;
/** Creates a new instance of PhonebookPdfView */
public PhonebookPdfView() {
}
protected void buildPdfMetadata(Map model, Document document,
HttpServletRequest request) {
document.addTitle("Phone Book");
document.addCreator("Arun Chhatpar");
}
protected void buildPdfDocument(
Map model,
Document doc,
PdfWriter writer,
HttpServletRequest req,
HttpServletResponse resp)
throws Exception {
List phonebook = (List) model.get("phonebook");
String title = "Phone Book";
Paragraph h1 = new Paragraph(title, HEADLINE_FONT);
h1.setAlignment(Paragraph.ALIGN_CENTER);
doc.add(h1);
doc.add(new Paragraph(" "));
doc.add(new Paragraph(" "));
doc.add(new Paragraph(" "));
// We create a table for used criteria and extracting information
PdfPTable table = new PdfPTable(NO_OF_COLUMNS);
int headerwidths[] = {20, 20, 20, 20, 20};
table.setWidths(headerwidths);
table.setWidthPercentage(100);
table.getDefaultCell().setBorderWidth(1);
table.getDefaultCell().setBorderColor(Color.black);
table.getDefaultCell().setPadding(3);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
table.addCell(new Phrase("Name", DATA_HEAD_FONT));
table.addCell(new Phrase("Home Phone", DATA_HEAD_FONT));
table.addCell(new Phrase("Work Phone", DATA_HEAD_FONT));
table.addCell(new Phrase("Cell Phone", DATA_HEAD_FONT));
table.addCell(new Phrase("Email", DATA_HEAD_FONT));
// We set the above row as title
// and adjust properties for normal cells
table.setHeaderRows(1);
table.getDefaultCell().setBorderWidth(1);
// We iterate now on the Phonebook list
Iterator it = phonebook.iterator();
while(it.hasNext()) {
PhonebookEntry pEntry = (PhonebookEntry) it.next();
table.addCell(new Phrase(pEntry.getFirstName() + " " +
pEntry.getLastName(),
TEXT_FONT));
table.addCell(new Phrase(pEntry.getHomeNumber(), TEXT_FONT));
table.addCell(new Phrase(pEntry.getWorkNumber(), TEXT_FONT));
table.addCell(new Phrase(pEntry.getCellNumber(), TEXT_FONT));
table.addCell(new Phrase(pEntry.getEmail(), TEXT_FONT));
}
doc.add(table);
doc.add(new Paragraph(" "));
doc.add(new Paragraph(" "));
doc.add(new Paragraph(" "));
doc.add(new Paragraph(" "));
String footerStr1 = "This example is developed to demonstrate the Spring
framework on the Geronimo Application Server.";
String footerStr2 = "This example is designed purely for
demonstrating the capabilities of the framework. It should under no
circumstances be used for production use.";
Paragraph footer1 = new Paragraph(footerStr1, FOOTER_FONT);
footer1.setAlignment(Paragraph.ALIGN_CENTER);
doc.add(footer1);
Paragraph footer2 = new Paragraph(footerStr2, FOOTER_FONT);
footer2.setAlignment(Paragraph.ALIGN_CENTER);
doc.add(footer2);
}
}
正如您所見,必須手動為 PDF 文檔創建內容。但是 Spring 的支持類使這一切變得更加簡單!
下一步是為 PDF 視圖定義視圖解析程序。
PDF 視圖的視圖解析程序
PDF 視圖的視圖解析程序類似於 Velocity 的視圖解析程序定義。您現在可能注意到了使用的是用於 PDF 控制器類的 MultiActionController。這樣做是希望向您展示另一種處理和解析 Spring 中的視圖的 方法。清單 13 對您來說應當很熟悉。
清單 13. 在 ApplicationContext 中添加 PDF 控制器和解析程序
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/*.do">phonebookController</prop>
<prop key="/*.htm">phonebookFlowController</prop>
<prop
key="/*.flow">phonebookFlowController</prop>
<prop
key="/addentry-mvc.act">addEntryFormController</prop>
<prop
key="/home-mvc.act">phonebookHomeController</prop>
<prop
key="/modifyentry-mvc.act">modifyEntryFormController</prop>
<prop
key="/deleteentry-mvc.act">deleteEntryFormController</prop>
<prop
key="/home.vel">phonebookVelocityController</prop>
<prop
key="/home.pdf">phonebookPDFController</prop>
</props>
</property>
</bean>
<bean id="phonebookPDFController"
class="phonebook.pdf.PhonebookPDFController">
<property name="methodNameResolver">
<bean
class="org.springframework.web.servlet.mvc.multiaction.
PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop
key="/home.pdf">handlePdf</prop>
</props>
</property>
</bean>
</property>
</bean>
<!-- View Resolver for PDF -->
<bean id="pdfViewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="2" />
<property name="basename" value="views-phonebook-pdf"/>
</bean>
像以前一樣,ResourceBundleViewResolver 需要用於類的屬性文件和用於視圖的 URL 定義。在本例 中,views-phonebook-pdf.properties 文件只包含一行:phonebook_pdfView.(class) =phonebook.pdf.PhonebookPdfView。
如果查看控制器類,它所使用的視圖與此屬性類中定義的一樣。這是 Spring 把此視圖類與該控制器 關聯起來的方法。
最後一步是為 PDF 請求添加 URL 映射。
為 PDF 請求添加 URL 映射
清單 14 向您展示了如何將 URL 映射添加到 web.xml 文件中以處理 *.pdf 請求。
清單 14. web.xml 文件中用於 *.pdf 的 URL 映射
<servlet-mapping>
<servlet-name>phonebook</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
現在是時候構建、部署和運行應用程序以便查看其運行情況。
構建、部署和運行它
本教程附帶的源壓縮文件包括所有類、配置文件和 Ant 構建文件(如果需要構建它)。本教程源文件 還有一個可部署的 .war 文件,其中包含所需的一切內容。您可以使用任意一種方法來獲取 phonebook.war 文件。
還需要確保 readme.txt 文件中所提及的所有 JAR 文件都在 <WORKSPACE>/phonebook/lib 目 錄中。請仔細閱讀該文件中的說明,並確保將所有必需的文件都復制到 <WORKSPACE>/phonebook/lib 中。注:有關構建和解壓縮這些文件的更多信息,您可以參閱本系列 教程的 第 2 部分 中的構建和打包說明。
使用 Geronimo 中的 Deploy New 工具部署 phonebook.war。如果一切運行正常,您將在 Geronimo Web 控制台上看到一條消息,顯示 Phonebook application deployed successfully。接下來,將浏覽器 指向新頁面:http://localhost:8080/phonebook/home.pdf。如果一切運行正常,您應當會在浏覽器中看 到用 Adobe Acrobat Reader 打開的 PDF。
Spring 框架的優點
您已經在本教程中了解了 Spring 框架所支持的各種視圖技術。大多數 Web 開發人員憑經驗都知道將 Web 內容和布局模塊化有多麼重要。雖然 JSP 是一種有用的技術,但是它有一些固有的缺點,例如缺少 布局或布局管理器的本地支持。模板技術(如 Tile)填補了該空白。Tile 提供了一種優秀的方法在單個 布局文件中定義 Web 應用程序的布局,該布局文件還使您可以輕松地根據業務需要進行更改。下面是一 些 Spring 對這些技術的支持所提供的優點:
明確分隔視圖與進入這些視圖中的內容
能夠分隔任務(使用 Spring 的模板使應用程序開發人員可以輕松地將精力完全集中到應用程序代碼 上,同時使 Web 開發人員可以設計最佳的 HTML 頁面而無需擔心如何呈現頁面)
能夠動態改變視圖技術
能夠測試單個視圖組件並且在開發周期早期就能准確指出錯誤
結束語
本教程向您詳細介紹了 Spring MVC 所支持的各種視圖技術。您看到了在樣例電話本應用程序中實現 這些技術有多麼簡單。Spring 的分層架構允許您根據應用程序的需要來引入恰到好處的功能,並且允許 您添加更多您熟悉的特殊技術。
使用模板定義和管理視圖的布局並不新鮮,但是將此技術與 Spring 結合使用十分簡單。您已經看到 ,Spring 證明了自己是一種強大的框架,可以為您處理很多復雜的問題,同時還提供了一種簡單的方法 來執行必要的任務(例如創建 PDF)。
下載
描述 名字 大小 下載方法 第 6 部分的源代碼 geronimo.spring6.source.zip 126KB HTTP 第 6 部分的 WAR 文件 geronimo.spring6.war.zip 7,149KB HTTP