程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Apache Geronimo和Spring框架,第5部分: Spring MVC

Apache Geronimo和Spring框架,第5部分: Spring MVC

編輯:關於JAVA

簡介:Spring Model-View-Controller(MVC)是 Spring 框架中應用最廣泛的模塊。它基於一種干淨 的設計,並提供了很多開箱即用的類。本教程是系列教程(共六部分)的第五部分,通過向電話本示例應 用程序中添加更多功能詳細介紹了 Spring MVC。在此過程中,您將了解如何利用豐富的 Spring MVC API 集中的有用類,如何定義易於理解的控制器來處理 Java™Server Page(JSP)的操作,如何擴展和 使用由 Spring MVC 提供的數據驗證類等等。並且一如既往,您將使用 Apache Geronimo Web 控制台來 簡化應用程序開發。

開始之前

本系列教程適合於需要了解 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 部分 展示了如何通過 Derby 數據庫添加 Java Database Connectivity (JDBC) 支持來擴展在 第 2 部分中獲得的 Geronimo 應用程序。您還了解了如何將對象關系映射(Object Relational Mapping ,ORM)集成到使用 iBATIS 的應用程序中。

第 4 部分 介紹了 Spring AOP 和 Spring Web 框架。使用 Spring AOP,任何受 Spring 框架管理的 對象都可變為面向方面的,並且本教程利用了通過 Spring AOP 提供的聲明式事務管理服務。

此部分是第 5 部分,介紹了 Spring Model-View-Controller (MVC)。該教程向您介紹了 Spring MVC 框架及 Web 視圖,使您可以了解 Spring MVC 的入門知識。

在本教程最後的第 6 部分中,介紹了如何通過 Spring 框架使用 JSP、Velocity、Tile 和 PDF 導出 功能。您將使用和體驗 Spring MVC 內置的各種 Web 視圖。

關於本教程

在本教程中,您將深入了解 Spring MVC 的詳細信息並擴展電話本樣例應用程序使其具有更多功能 — — 特別是修改和刪除功能。本教程將向您展示如何使用豐富的 Spring MVC API 集中的一些最有用的類 ,並且將定義控制器來處理 JSP 頁面的操作。

您還將了解如何擴展和使用 Spring MVC 提供的數據驗證類。您將使用標准標記庫以及 Spring 數據 綁定將數據對象綁定到將要創建的 JSP 視圖。Standard Taglib 已被選定,它將實現 1.1 版的 JSP 標 准標記庫 (JSTL),因為使用 JSTL 庫可以輕松地實現 JSP 中所需的迭代操作。JSTL 是 Sun Microsystems 提供的一個易於使用的 JSP 標記庫,它封裝了 Web 應用程序所必需的大多數常見功能, 例如迭代和條件語句(注:您可以選擇不使用 JSTL,但 JSP 將不會像本教程中所示的這些 JSP 這樣干 淨和模塊化)。

先決條件

本教程假定您熟悉面向對象的編程 (OOP) 並且熟知 Java 2 Platform, Enterprise Edition (J2EE) 、Java EE 術語和基本的 MVC 概念。由於本教程的目的在於了解 Spring MVC,因此只簡要討論了大多數 基本的 MVC 概念。

系統要求

您的系統需要至少滿足以下要求才能繼續學習本系列教程:

The Spring Framework v1.2.8 —— 具有所有依賴性的壓縮文件。

Apache Geronimo 1.1 —— Geronimo 是 Apache 的 J2EE 認證應用服務器。

Standard Taglib API —— 您將在 JSP 中使用 JSTL 標記,並且需要使用本教程末尾的 下載 部分 中附帶的壓縮文件中的 .jar 文件。

標准 JSTL 庫 —— 當前版本為 1.1.2。

Apache Derby 數據庫 —— 本教程使用 Derby,該數據庫是開源的輕量級數據庫。Derby 是嵌入到 Geronimo 1.1 裡的,因此不需要再單獨安裝。

Apache Ant —— 確保正確配置 Ant 並且其 /bin 目錄位於 Path 系統變量中。

Java 1.4.2 —— 確保 Java 安裝並運行在系統中。

安裝和配置軟件

此部分包含安裝和配置開發、部署和運行示例應用程序所必需的軟件的說明。

安裝 Spring 框架和 Geronimo。要使樣例代碼運行,需要安裝運行 Apache Geronimo 和 Spring 框 架。有關詳細的安裝說明,請返回至 第 2 部分。

安裝 Apache 和 Spring taglib 的 Standard Taglib。JSP 主頁將動態讀取數據並將其填充到一張表 中。您將了解如何使用一些來自 JSTL 和 Spring 標記庫的標記使 JSP 頁面變得更干淨。將主要使用 JSTL 來消除主頁中的循環 Java 代碼。要開發的應用程序將主要使用 Spring 標記庫把表單與命令對象 綁定在一起。

下載標准 JSTL 庫。當前版本是 1.1.2,並且將使用此版本用於您的應用程序。它實現 Sun 的 JSTL 1.1 規范。下載如上所示的壓縮文件(標准 JSTL 庫鏈接),並且將其保存到硬盤驅動器中的某個位置。 下載文件後,將其解壓縮到應用程序所在的驅動器中。(例如,我把它安裝到 K: 驅動器中。)解壓縮文 件應當會創建名為 jakarta-taglibs-standard-1.1.2 的目錄。(在我的驅動器中,此安裝位於 K:\ jakarta-taglibs-standard-1.1.2 目錄中。)解壓縮後,將 jstl.jar 和 standard.jar 文件復制到 <WORKSPACE>/phonebook/lib 目錄中。Spring 標記庫已包含在 Spring 發布版中。

復制標記庫描述符(Tag Library Descriptor,TLD)。將 Standard Taglib 目錄中的 c.tld 以及 Spring 框架發布版目錄中的 spring.tld 復制到 WEB-INF 文件夾中。您可以從剛復制到庫目錄中的 .jar 文件中提取標記庫描述符(Tag Library Descriptor,TLD)。

Apache Derby 數據庫。預打包在 Geronimo 1.1 中的 Derby 數據庫安裝無需任何特殊配置。如果您 已經在本系列教程的 第 3 部分 或 第 4 部分 中創建了數據庫和表,則無需做任何操作。如果尚未這樣 做,請按照第 3 部分中關於創建數據庫和表的說明進行操作。

為應用程序定義數據模型和設置數據庫。您將使用在本系列教程的第 3 部分中創建的數據庫。數據模 型也是一樣的。如果您已經按照本系列教程執行過操作,那麼所有的部分都應該設置好了。如果沒有,則 應當按照 第 3 部分 中的說明進行操作,然後再嘗試部屬此應用程序。

Spring MVC 簡介

在此部分中,您將簡要了解將 MVC 設計模式用於 Web 應用程序的優點。然後了解 Spring 框架的 MVC 實現。

為什麼使用 MVC?

很多應用程序的問題在於處理業務數據和顯示業務數據的視圖的對象之間存在緊密耦合。通常,更新 業務對象的命令都是從視圖本身發起的,使視圖對任何業務對象更改都有高度敏感性。而且,當多個視圖 依賴於同一個業務對象時是沒有靈活性的。讓我們來研究一下 MVC 如何解決這些問題。

MVC 作為設計模型

MVC 是一種著名的設計模式,特別是在 Web 應用程序領域。模式全都是關於將包含業務數據的模塊與 顯示模塊的視圖解耦的。這是怎樣發生的?視圖(例如,JSP 頁面)怎樣能夠與其模型(例如,包含數據 的 JavaBean)解耦?記得這句格言麼?一個層次的重定向幾乎可以解決計算機業中的所有問題。確實, 在模型和視圖之間引入重定向層可以解決問題。此重定向層是控制器。控制器將接收請求,執行更新模型 的操作,然後通知視圖關於模型更改的消息。依賴於模型的狀態並且依賴於請求的控制器可以決定要顯示 哪個視圖。圖 1 演示了這種模式。

圖 1. MVC 設計架構

Spring MVC 的強大之處

Spring MVC 實現了即用的 MVC 的核心概念。它為控制器和處理程序提供了大量與此模式相關的功能 。並且當向 MVC 添加反轉控制(Inversion of Control,IoC)時,它使應用程序高度解耦,提供了通過 簡單的配置更改即可動態更改組件的靈活性。Spring MVC 為您提供了完全控制應用程序的各個方面的力 量。

Spring 的 Web MVC 模塊是圍繞 DispatcherServlet 而設計的。DispatcherServlet 給處理程序分派 請求,執行視圖解析,並且處理語言環境和主題解析,此外還為上傳文件提供支持。

DispatcherServlet 通過使用處理程序映射來決定哪一個處理程序應當處理傳入的請求。處理程序映 射只是用於標識使用哪一個處理程序來處理特定 URL 模式的映射。處理程序是只有一種方法 ModelAndView handleRequest(request,response) 的控制器接口的實現。Spring 還有一些可用的高級處 理程序實現;其中一個重要的高級處理程序實現是 SimpleFormController,它提供了將命令對象綁定到 表單、對其執行驗證等功能。

您已經在本系列教程的先前教程中使用了 DispatcherServlet 和簡單的處理程序。在下一個部分中, 將使用 SimpleFormController 並說明 Spring MVC 提供的各種即用功能。

用 Spring MVC 擴展電話本應用程序

在此部分中,將擴展電話本樣例應用程序以使用 Spring MVC。

對電話本應用程序進行修改

在本教程中,您將對電話本應用程序進行下列修改:

開發一個用於主頁的控制器。此控制器將處理來自主頁的所有操作。

使用表單支持命令對象將電話本條目填充到主頁中。您將看到支持命令對象如何高效地解決從主頁本 身獲取電話本條目列表的問題。

開發一個用於 Add Entry 頁面的控制器。

將 Add Entry 頁面中的表單元素綁定到命令對象 (PhonebookEntry)。

添加執行表單提交驗證的代碼。

將成功驗證的條目添加到數據庫中。

在執行這些更改的過程中,將按照定義新控制器和新 JSP 頁面的相同步驟添加修改和刪除條目的功能 。

工作區的目錄結構

圖 2 向您展示了應用程序的布局。從 下載 部分下載源壓縮文件,並最好將其解壓縮到根目錄中。

圖 2. 解壓縮源文件後的應用程序目錄結構

這裡將會發生什麼情況?

在開始開發本教程中使用的各個組件之前,請先來簡要了解一下各個組件的角色。圖 3 演示了 DispatcherServlet 如何與 Spring MVC 類結合使用。此圖中顯示的概念和流程是將要在本教程中創建的 所有控制器和 JSP 頁面的基礎。

圖 3. 顯示 Spring MVC 發揮作用的事件流

在任何 Web 應用程序中,GET 請求通常都表示一個表單打開事件,POST 請求表示一個表單提交事件 。Spring SimpleFormController 是使用相同的設計原理來設計的。下面是此控制器中的一些重要方法:

formBackingObject():當控制器收到一個表示表單打開的 GET 請求時調用此方法。這是可以創建並 返回帶有在表單被打開時需要顯示的數據的命令對象的位置。

Validate():表單被提交後,控制器將先把表單元素綁定到命令對象上。綁定成功後,它將調用驗證 器上的驗證方法並傳遞剛創建的命令對象。

onSubmit():如果命令對象驗證成功,則調用此方法。這是可以對輸入的數據采取操作的位置,例如 將數據保存到持久穩固的庫中。

此外,請記住這些方法是根據請求裝入所有 JSP 頁面的方法,並且是控制器處理操作的方法。掌握了 這些信息,您就已經准備好開始開發構成此應用程序的組件了。

引入

PhonebookHomeController

SimpleFormController

類是 Spring MVC 的 FormController 的具體類。它在用相應的 命令對象創建表單時提供支持。SimpleFormController 允許您指定命令對象、表單的視圖名稱、表單提 交成功時需要顯示給用戶的頁面的視圖名稱等等。它還允許在驗證錯誤的情況下重新提交到表單視圖。

通過覆蓋 formBackingObject 方法可以定制這個 SimpleFormController 類。您將使用此控制器將電 話本條目填充到主頁中。如前述,formBackingObject 是可以用於實現此目的的方法。另請注意,您將使 用 PhonebookEntry 對象作為命令對象。清單 1 顯示了如何定義此控制器。

清單 1. 定義 PhonebookHomeController

public class PhonebookHomeController extends SimpleFormController{

   private IPhonebookDataProvider pbDataProvider;

   protected ModelAndView onSubmit(Object command) throws Exception {
     return new ModelAndView(new RedirectView(getSuccessView()));
   }

   public IPhonebookDataProvider getPbDataProvider() {
     return pbDataProvider;
   }

   public void setPbDataProvider(IPhonebookDataProvider pbDataProvider) {
     this.pbDataProvider = pbDataProvider;
   }

   protected Object formBackingObject(HttpServletRequest request) throws  Exception {
     WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(request.getSession().
getServletContext());
     IPhonebookDataProvider pb = (IPhonebookDataProvider) ctx.getBean ("phonebook");
     List phonebookEntries = pb.getPhonebookEntries();
     return phonebookEntries;
   }
}

在 清單 1 中可以看到 formBackingObject 具有提供電話本條目列表的邏輯。但由於 formBackingObject 需要獲取此列表,因此它需要使用一個 IPhonebookDataProvider 類型的對象。您將 使用 IoC 並將此屬性注入控制器中,下一部分將對其加以說明。現在,了解一下如何配置控制器使其處 理以下 /home-mvc.act URL 模式。

讓應用程序上下文知道新控制器的存在

下一步是讓在 phonebook-servlet.xml 中定義的應用程序上下文知道剛定義的新控制器的存在。此步 驟包括定義兩個 bean,如清單 2 所示。

清單 2. 在 phonebook-servlet.xml 中添加 PhonebookHomeController 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="/home-mvc.act">phonebookHomeController</prop>
           <prop
key="/addentry-mvc.act">addEntryFormController</prop>
           <prop
key="/modifyentry-mvc.act">modifyEntryFormController</prop>
           <prop
key="/deleteentry-mvc.act">deleteEntryFormController</prop>
       </props>
     </property>
   </bean>

<bean id="phonebookHomeController"
class="phonebook.controller.PhonebookHomeController">
     <property
name="sessionForm"><value>true</value></property>
     <property
name="bindOnNewForm"><value>true</value></property>
     <property
name="formView"><value>home-mvc</value></property>
     <property
name="successView"><value>addentry-mvc</value></property>
     <property
name="commandName"><value>phonebookEntries</value>
</property>
     <property
name="commandClass"><value>java.util.List</value></property>
      <property name="pbDataProvider">
       <ref bean="phonebook"/>
     </property>
   </bean>

您在本系列教程的先前部分中看到了如何將 URL 模式映射到特定的控制器。現在在這裡的第一個 bean 定義中要做同樣的操作。

第二個 bean 是控制器配置,使應用程序上下文知道新控制器的存在。下面細分了 Spring MVC 建議 使用的 bean 定義的其他重要屬性:

sessionForm:此屬性用於表示此表單是不是會話表單。如果是,控制器將在第一次請求時調用 formBackingObject 方法並保留會話中返回的命令對象。如果需要處理驗證錯誤同時仍保留用戶可能已經 輸入的任何數據,則使用此屬性十分便利。

bindOnNewForm:如果為真,控制器還將執行新表單與命令對象的綁定。

formView:表單視圖是在對此控制器發出請求時應當打開的視圖。它還是任何驗證失敗的默認視圖。

successView:成功處理 onSubmit 方法後將打開此視圖。

commandName:這是綁定後控制器所創建的命令對象的名稱,或者是可在 JSP 頁面中訪問的 formBackingObject 方法所返回的命令對象的名稱。

commandClass:此屬性用於指定命令對象的類。

pbDataProvider:這是一個用戶定義的屬性,表示您定義的 bean。需要使用此對象來訪問數據庫中的 數據。您可以將此屬性設為在 第 4 部分 中創建的 AOP 代理的對象,以使用事務和跟蹤。

定義和配置主頁控制器的工作現在已經完成。下一步是啟用 MVC 的 home-mvc.jsp 頁面。

Spring MVC 對 JSP 的支持

此部分向您展示了 Spring Framework 在 JSP 方面提供的支持。您將了解 Spring 如何將 Java 對象 作為命令對象傳遞給視圖(在本例中為 JSP 頁面)。請繼續前進並更改主頁以使其啟用 MVC。

讓視圖 (home.jsp) 啟用 MVC

對由先前在 第 4 部分 中實現的主頁演變而來的 home-mvc.jsp 不需要做大量更改。但是,如果查看 清單 3,則會注意到此頁面中的所有 Java 代碼幾乎都已被刪除。它還使用了 JSTL(特別是 c:forEach 標記)來實現重復操作。注意 c:forEach 標記的參數是 ${phonebookEntries}。此對象的名稱與配置主 頁控制器時指定的命令名稱相同。需要注意的另一個更改是調用 addentry-mvc.jsp 的 JavaScript 代碼 。在本例中指定的操作是 /phonebook/addentry-mvc.act,它將映射到在後面部分中定義的 AddEntryFormController。另請注意,表單提交方法為 GET。同先前說明的一樣,GET 將表示一個表單打 開。

清單 3. 做出更改來為主頁啟用 MVC

...
<html>

<script type="text/javascript">
function goToAddEntryPage()
{
   document.myForm.action="/phonebook/addentry-mvc.act";
   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>
...
   <form name="myForm" action="" method=post>

     <!-- 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>

       <c:forEach items="${phonebookEntries}" var="pbEntry">

       <TR>
         <TD align=center><input type=radio name="data_i"
alt="Select to Modify or Delete" align="middle"
onclick="javascript:setId(${pbEntry.entryID},${pbEntry.rowID})"></TD>
         <TD align=center><c:out
value="${pbEntry.firstName}"/>&nbsp;<c:out
value="${pbEntry.lastName}"/></TD>
         <TD align=center><c:out
value="${pbEntry.homeNumber}"/></TD>
         <TD align=center><c:out
value="${pbEntry.workNumber}"/></TD>
         <TD align=center><c:out
value="${pbEntry.cellNumber}"/></TD>
         <TD align=center><c:out
value="${pbEntry.email}"/></TD>
       </TR>

       </c:forEach>

       <input type="hidden" name="entryID" value="" />
       <input type="hidden" name="rowID" value="" />

     </TABLE>

     <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>
...

此時,對主頁的操作就已完成。接下來,您將為 addEntry 頁面啟用 MVC。

引入 AddEntryFormController

需要 AddEntry 控制器執行的操作包括裝入頁面和將新條目保存到數據庫中。這些操作將使用您在 homeController 中使用的同一個 SimpleFormController 類。首先來看看用於此類的清單 4。

清單 4. 定義與主頁控制器類似的 AddEntryFormController

public class AddEntryFormController extends SimpleFormController{

   private IPhonebookDataProvider pbDataProvider;

   protected ModelAndView onSubmit(Object command) throws Exception {

     System.out.println("ON SUBMIT CALLED......");
     PhonebookEntry phonebookEntry = (PhonebookEntry)command;
     pbDataProvider.addEntry(phonebookEntry);
     return new ModelAndView(new RedirectView(getSuccessView() +".act"));

   }

   protected Object formBackingObject(HttpServletRequest request) throws  Exception {
     PhonebookEntry phonebookEntry = new PhonebookEntry();
     return phonebookEntry;
   }

   public IPhonebookDataProvider getPbDataProvider() {
     return pbDataProvider;
   }

   public void setPbDataProvider(IPhonebookDataProvider pbDataProvider) {
     this.pbDataProvider = pbDataProvider;
   }

}

正如您所見,清單 4 中的代碼與為主頁控制器執行的操作沒有什麼不同。這裡惟一的改變是在表單打 開時返回一個空命令對象,因為在表單打開時無需填充任何數據。此外,這裡添加了處理 onSubmit 事件 的邏輯,因為在執行此操作時需要把條目添加到數據庫中。在將用戶輸入保存到數據庫中之前先驗證用戶 輸入不是很好麼?請繼續學習並引入 PhonebookEntryValidator 類來具體執行此操作。

引入 PhonebookEntryValidator

使用此類的惟一一個目的是驗證用戶在 Add Entry 頁面中輸入的數據。您立刻就會認識到添加數據驗 證代碼並將驗證錯誤作為對象返回是多麼地干淨和輕松。清單 5 顯示了 PhonebookEntryValidator 類的 代碼。

清單 5. 用於處理數據驗證的驗證類

public class PhonebookEntryValidator implements Validator {
   public boolean supports(Class clazz) { return
clazz.equals(PhonebookEntry.class); }

   public void validate(Object o, Errors errors) {
     validatePhonebookEntry((PhonebookEntry)o, errors);
   }

   public void validatePhonebookEntry(PhonebookEntry pbEntry, Errors errors)  {

     if ("".equals(pbEntry.getFirstName()) || pbEntry.getFirstName() == null)  {
       errors.rejectValue("firstName",null, "First name cannot be
empty");
       }
     if ("".equals(pbEntry.getLastName()) || pbEntry.getLastName() == null)  {
       errors.rejectValue("lastName",null, "Last name cannot not be  empty");
       }
   }
}

根據 Spring MVC 規范,驗證類需要實現 Validator 接口。清單 5 中定義的類就是這樣做的。在這 裡,最重要的方法是 supports() 方法,它用於確保此驗證程序僅用於您的命令對象 (PhonebookEntry) 。

validate 方法是需要添加驗證代碼的位置。名字和姓氏不允許為空。validate 方法將收到一個已與 表單綁定的命令對象和一個 Errors 對象。通過 Errors 對象可以拒絕用戶已經輸入的值。注意, rejectsValue 方法的第一個參數應當與命令對象的對應屬性完全匹配。Spring 將使用反射 API 把錯誤 與對應的 Command 對象屬性直接關聯起來。

下一步將向您展示如何將此驗證程序動態地注入 addEntry 控制器中。

讓應用程序上下文知道用於 addEntry 的新控制器的存在

下一步是更改 phonebook-servlet.xml 文件以映射這個新控制器的 URL。清單 6 中突出顯示了該配 置中的重要行。

清單 6. 在應用程序上下文文件中添加 addEntryFormController 的配置

<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>
       </props>
     </property>
   </bean>

<bean id="phonebookEntryValidator"
class="phonebook.controller.PhonebookEntryValidator"/>

<bean id="addEntryFormController"
class="phonebook.controller.AddEntryFormController">
     <property
name="sessionForm"><value>true</value></property>
     <property
name="bindOnNewForm"><value>false</value></property>
     <property
name="commandName"><value>phonebookEntry</value></property>
     <property
name="commandClass"><value>phonebook.dao.PhonebookEntry</value><
/property>
     <property name="validator"><ref
bean="phonebookEntryValidator"/></property>
     <property
name="formView"><value>addentry-mvc</value></property>
     <property
name="successView"><value>addentry-mvc</value></property>
     <property name="pbDataProvider">
       <ref bean="phonebook"/>
     </property>
   </bean>

如果查看 ID 為 phonebookEntryValidator 的第二個 bean 定義,它定義了將與 addEntryFormController(在下一個 bean 定義中定義)結合使用的 validator bean。 addEntryFormController bean 將定義用於應用程序的 Add Entry 頁面的控制器。其中需要注意的最重 要的屬性是 validator。當 ApplicationContext 讀取此屬性時,它使控制器知道還必須使用 phonebookEntryValidator bean 來驗證它所處理的視圖。

現在可以為 addentry-mvc.jsp 頁面啟用 MVC。

Spring MVC 中的數據綁定

在此部分中,您將更改 addEntry.jsp 頁面使其與 Spring MVC 協作。您還將看到如何使用 Spring MVC 標記庫把數據對象與視圖(JSP 頁面中的元素)綁定起來。

為第二個視圖頁面 (addEntry.jsp) 啟用 MVC

將使用 JSTL 標記庫中標記把命令對象與輸入字段綁定起來。Spring MVC API 附帶了它自己的標記庫 描述符 (TLD) 文件。使用該文件可以實現命令對象的動態綁定。首先,看一看清單 7 中的這段 JSP 代 碼。

清單 7. addEntry.jsp 中的 MVC 綁定代碼

<%@ include file="/WEB-INF/jsp/header.jsp" %>
...
<html>

<script type="text/javascript">
  function doSave()
  {
   document.myForm.pageAction.value="ADD";
   document.myForm.action="/phonebook/addentry-mvc.act";
   document.myForm.submit();
  }

  function doReset()
  {
   document.myForm.reset();
  }

  function goHome()
  {
   document.myForm.action="/phonebook/home-mvc.act";
   document.myForm.method="GET";
    document.myForm.submit();
  }

</script>
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>Phone Book</title>
   </head>
   <body>
   <form name="myForm" action="" method=post>
   <fieldset>
     <!--The Heading of the Phone Book Page-->
     <h1 align=center>Phone Book - Add Entry</h1>

     <BR>
     <BR>
     <!-- The table containing phone book contents. -->

     <TABLE border="1" width="100%">

       <TH width="15%" align=center>First Name</TH>
       <TH width="15%" align=center>Last 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>

       < spring:bind path="phonebookEntry">
        <font color="red">
         <b><c:out value="${status.errorMessage}"/></b>
        </font>
       </spring:bind>

       <spring:nestedPath path="phonebookEntry">
       <!-- Add Spring bindings here -->
       <TR>
         <spring:bind path="firstName">
           <TD align=center><input type=text
name="${status.expression}" value="${status.value}" alt="Enter First Name"
align="middle"/></TD>
           <c:if test="${status.error}">
                <span class="error"><c:out
value="${status.errorMessage}"/></span>
           </c:if>
         </spring:bind>

         <spring:bind path="lastName">
           <TD align=center><input type=text name="<c:out
value="${status.expression}"/>" value="<c:out value="${status.value}"/>"
alt="Enter Last Name" align="middle"/></TD>
           <c:if test="${status.error}">
       <span class="error"><c:out
value="${status.errorMessage}"/></span>
           </c:if>
         </spring:bind>
...
       </spring:nestedPath>
     </TABLE>

     <table align=center>
       <!-- The row containing command buttons -->
       <TR align=center>
         <TD><input type=button name="Save" value="Save the Entry"
onclick="javascript:doSave()"></TD>
         <TD><input type=button name="Reset" value="Reset"
onclick="javascript:doReset()"></TD>
         <TD><input type=button name="Home" value="Go to Home Page"
onclick="javascript:goHome()"></TD>
       </TR>
...

注意 清單 7 中所示的表單中的 spring:bind 標記。spring:bind 標記把標記中包含的表單元素與 path 屬性所標識的對應屬性綁定起來(參見清單 8)。

清單 8. 來自清單 7 的 spring:bind 標記

<spring:nestedPath path="phonebookEntry">

<spring:bind path="firstName">
           <TD align=center>>input type=text
name="${status.expression}" value="${status.value}" alt="Enter First Name"
align="middle"/></TD>
           <c:if test="${status.error}">
                <span class="error"><c:out
value="${status.errorMessage}"/></span>
           </c:if>
</spring:bind>

此綁定規范將把輸出的表單元素與命令對象 phonebookEntry 的 firstName 屬性綁定起來。注意,如 果不在嵌套的路徑內指定 Spring 綁定,則必須訪問作為 phonebookEntry.firstName 的 firstName 屬 性。

Status 是一個可用於描述綁定狀態的 JSP 頁面的特殊變量。下面是其各種屬性的簡要介紹:

Status.expression 包含正被綁定的屬性的名稱。

Status.value 包含可以用於填充輸入字段的屬性的值。

Status.error 標識綁定是否導致了錯誤。

如果綁定導致了錯誤,Status.errorMessage 將用於包含綁定錯誤消息。

現在,主頁和 Add Entry 頁面完全啟用了 MVC。接下來,您將看到如何創建具有添加、修改和刪除功 能的新頁面。

定義新的修改和刪除頁面

了解了使用 Spring MVC 創建主頁和 Add Entry 頁面所需的簡單步驟。您將使用相同的步驟來創建主 頁和 Add Entry 頁面以創建修改頁面和刪除頁面。

最後幾步

幾乎完成了!需要更改 Web 配置文件把應用程序集成到 Geronimo 中。此部分向您展示了如何使應用 服務器知道啟用了 MVC 的頁面的存在。通過在 web.xml 文件中添加新的 URL 映射完成此操作。

啟用了 Spring 的頁面的 URL 映射

您只有最後一件事要做:在 web.xml 中添加 *.act 的映射。要使代碼保持干淨,需要定義一個新的 URL 映射。采用這種方法,如果您是一直按照本系列教程操作的,則仍能夠訪問在先前教程中創建的其他 頁面。

在 web.xml 中添加 *.act 作為新映射

這是一個簡單的映射,用於讓應用服務器知道傳入 *.act 的任何請求都應當被轉發給默認的 Spring DispatcherServlet。DispatcherServlet 將通過讀取在先前部分中定義的配置知道哪一個控制器已被配 置。清單 9 顯示了 web.xml 文件中增加的這段代碼。

清單 9. 將 *.act 映射到 DispatcherServlet

 <servlet>
   <servlet-name>phonebook</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
   <load-on-startup>1</load-on-startup>
  </servlet>

   <servlet-mapping>
   <servlet-name>phonebook</servlet-name>
   <url-pattern>*.act</url-pattern>
  </servlet-mapping>

此映射將把匹配 *.act 的 URL 定向到電話本分派程序 servlet。

現在是時候構建、部署和運行應用程序並查看其運行情況了。

Spring MVC 發揮作用

好的!隨著 Web 應用程序的開發完成了 MVC 的啟用。現在需要構建和運行它。此部分向您展示了查 看 Spring MVC 發揮作用需要采取的步驟。

構建、部署和運行應用程序

本教程附帶的源壓縮文件包括所有必要的類、配置文件和 Ant 構建文件(如果要構建它)(有關鏈接 ,請參閱 下載)。壓縮文件中還有一個包括所有必需內容的可部署 .war 文件。

必須確保 readme.txt 文件中提及的所有 .jar 文件都位於 <WORKSPACE</phonebook/lib 目錄 中。請參閱本系列教程的 第 2 部分 中的構建和打包說明(如果需要)。

現在,使用 Geronimo 中的 Deploy New 工具來部署 phonebook.war 文件。如果一切都按照計劃運行 ,您將在 Geronimo Web 控制台中看到以下消息:Phonebook application deployed successfully。

現在只需將浏覽器指向新頁面:http://localhost:8080/phonebook/home-mvc.act。如果一切運行正 常,主頁應當顯示如圖 4 所示的內容。

圖 4. 應用服務器中運行的 home-mvc.act

您應當會在 Geronimo 控制台中看到系統輸出消息,顯示在本系列教程的 第 4 部分 中定義的所有建 議都被執行。

添加錯誤處理

您可以將一般錯誤處理輕松地添加到此應用程序中。必需的全部操作包括在 web.xml 中指定錯誤頁面 條目並定義一個錯誤頁面。清單 10 顯示了 web.xml 中的條目。

清單 10. 配置錯誤頁面來處理異常

 <error-page>
   <exception-type>java.lang.Throwable</exception-type>
   <location>/WEB-INF/jsp/errorpage.jsp</location>
  </error-page>

  <error-page>
   <exception-type>500</exception-type>
   <location>/WEB-INF/jsp/errorpage.jsp</location>
  </error-page>

在拋出任何異常或出現 500 錯誤代碼的情況下,清單 10 中的配置將把流程定向到 errorpage.jsp。 Errorpage.jsp 僅以一種友好的方式顯示錯誤消息(參見清單 11)。

清單 11. ErrorPage 的 JSP 代碼

...
<TR>
  <TD>Your request to the following URI: ${pageContext.errorData.requestURI}
has failed. </TD>
</TR>

<TR>
  <TD>Status code: ${pageContext.errorData.statusCode}</TD>
</TR>

<TR>
  <TD>Exception: ${pageContext.errorData.throwable}</TD>
</TR>
...

您將在本教程所提供的源代碼中看到 Errorpage.jsp 以及其他 JSP(請參閱 下載)。還對 web.xml 做了更改。您只需編譯並重新部署應用程序即可看到它運行。

使用 Spring 框架的優點

您的操作已經涉及到了 Spring 框架中最常用的一個模塊:Spring MVC。它提供了一整套用於構建 Web 應用程序的 MVC 功能。Spring 的可移植架構允許您使用內置 MVC 框架或其他框架(例如 Struts) 。

使用 Spring 的 Web 模塊的優點包括:

清晰的角色劃分:Spring MVC 有一組經過良好定義的類,用於控制器、驗證程序、命令對象、表單對 象、處理程序、視圖解析程序等等。這些類中的每個類都是一個履行單獨任務的專門對象。

能夠將框架類和應用程序類配置為 JavaBean。

可重用的業務代碼:Spring 的架構允許您使用現有的業務對象作為命令對象或表單對象。使用諸如 Struts 之類的其他框架都不可能輕松地實現這一點。

自定義綁定和驗證:Spring MVC 定義的對象的綁定規則都是可以高度自定義的。您可以直接將視圖綁 定到 Java 對象上,而不是綁定到字符串上。

靈活的視圖解析及處理程序映射:使用 Spring MVC,您可以使用大量視圖解析策略。通常使用它附帶 的視圖解析策略,但是您能夠定義自己的策略(基於您的要求)並將其插入。

簡單的本地解析和主題解析。

並且最後,一個簡單但是功能強大的標記庫將嘗試避免花費任何代價來生成 HTML,使在構造代碼方面 具有最大的靈活性。

結束語

本教程向您展示了 Spring MVC 模塊是基於一種干淨清晰的設計的。分發時它預打包了很多可以在應 用程序中直接使用的類。Spring 的架構的妙處在於它可以輕松地引入您認為應用程序所必需的那些功能 。

本教程中引入的控制器十分易於理解。最困難的部分是將命令對象綁定到視圖上。Geronimo Web 控制 台再一次使執行部署應用程序的任務變得更加輕松。

在本系列教程的最後一部分第 6 部分中,您將繼續了解與 MVC 相關的技術。您將更詳細地了解 JSP ,還將更深入地研究 Spring MVC,方法為論證 MVC 相對於特定實現的獨立性(例如,JSP 與其他 UI 技 術對比),以及這種獨立性為 Geronimo 應用程序提供的優勢。將用 Spring MVC 中構建的各種 Web 視 圖做試驗,演示 Velocity 和 Tile 等模板技術。第 6 部分附帶了一個示例,展示怎樣才能使用 Spring MVC 的內置 PDF 支持類導出 PDF。敬請關注!

下載

描述 名字 大小 下載方法 第 5 部分的源代碼 geronimo.spring5.source.zip 117KB HTTP 第 5 部分的 .war 文件 geronimo.spring5.war.zip 4,213KB HTTP
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved