Spring Web Flows

實(shí)踐指南

原文:Spring Web Flows - A Practical Guide

Author: Erwin Vervaet (Mail To)

translator: Dorian Shi (Mail To)

這篇文章介紹了 Spring Web Flows。用實(shí)例來說明基于 Web Flow 的原理。文章也提供了一個(gè)使用 Web Flow 和 Spring 框架來構(gòu)建 Web 應(yīng)用的實(shí)踐指南.

假定讀者已經(jīng)了解一些 J2EE Web 應(yīng)用、XML、Spirng 框架,當(dāng)然還有 Spring 的 Web MVC 框架。你可以查看資源來了解這些信息。

介 紹

在一個(gè) Web 應(yīng)用程序中傳統(tǒng)的定義一個(gè)頁面流程是一個(gè)一點(diǎn)也不直觀的過程。像 Struts 和 Spring 這樣的框架促使你把頁面流程放進(jìn)單獨(dú)的控制器和視圖中。舉個(gè)例子:Struts 會把請求映射到一個(gè) Action。這個(gè) Action 會選擇一個(gè)視圖并轉(zhuǎn)發(fā)到這個(gè)視圖。雖然這是一個(gè)簡單并且實(shí)用的方法,但它有一個(gè)主要的缺點(diǎn):到 Action 的定義文件 struts-config.xml 中看看,Web 應(yīng)用程序的所有頁面流程一點(diǎn)也不清晰。因?yàn)?Action 不能簡單的被重用,靈活性同樣也會遭到損害。

Spring Web MVC 框架提供了一個(gè)細(xì)微的高級功能:表單控制器執(zhí)行一個(gè)預(yù)先定義好的工作流。有兩種控制器提供了這一特性:SimpleFormController 和 AbstractWizardFormController。然而,這些仍舊是一種一般化的硬編碼的工作流概念。

到這里就要引入 Spring 的 Web Flow 了。它允許你用清晰簡單的方式展現(xiàn) Web 應(yīng)用的頁面流程。就像我們要看到的一樣,他有許多有點(diǎn):

  • 一個(gè) Web 應(yīng)用的頁面流程通過 Web Flow 定義文件(一個(gè) XML 文件)清楚的展現(xiàn)出來。
  • Web Flow 可以被設(shè)計(jì)成自包含的(slef contained)。這意味著允許你在很多情形中把你應(yīng)用程序的一部分看成一個(gè)組件并且使之重用。
  • Web Flow 可以在一個(gè) Web 應(yīng)用中總是使用一樣的手法來定義任何合理的頁面流程。你無需在非常特殊的情況下被迫使用專門的控制器。

現(xiàn)在 Web Flow 有足夠的能力表示由一系列 State 組成的一個(gè) Web 流程。State 是事件發(fā)生的流程點(diǎn):舉例來說就是顯示一個(gè)視圖或者執(zhí)行一個(gè) Action。每個(gè) State 有一個(gè)或多個(gè) transitions ,他們習(xí)慣于從一個(gè) State 轉(zhuǎn)到另一個(gè) State 。一個(gè) transitions 被一個(gè) Event 所觸發(fā)。 為了讓你對 Web 流程有一個(gè)大概的印象,下面這段 XML 定義了一個(gè) Web 流程,大致等效于實(shí)現(xiàn)一個(gè) SimpleFormController 的工作流 。對于這個(gè) Web 流程的原理將在本文的稍后詳細(xì)說明。

 

熟悉業(yè)務(wù)過程管理(BPM) 的讀者將認(rèn)識到 Web 流程是普通工作流的一個(gè)特例,所以他們在理論上可以使用像 JBMP(請查看資源) 來實(shí)現(xiàn)一般化的 BMP 系統(tǒng)。既然簡單是 Spring Web Flow 的重要設(shè)計(jì)目的,所以它不會去使用這種一般化的工作流引擎。在我們 Web 應(yīng)用中,我們會用一個(gè)簡單的 Web 流程來描述一個(gè)頁面的流程。

本文的剩余部分會用一個(gè)實(shí)例來說明這方面內(nèi)容,可以使用這個(gè)連接來下載源代碼:PhoneBook.war.zip 。現(xiàn)在下載并解壓縮這個(gè)文件可能是個(gè)好注意,它能夠在你閱讀文章的時(shí)候幫助你學(xué)習(xí)。

 

實(shí) 例

實(shí)例是一個(gè)使用 Spring Web Flow 的電話簿應(yīng)用程序,我們將使用圖解的方式來說明他的功能意圖。這是一個(gè)典型的公司內(nèi)網(wǎng)的應(yīng)用程序,你會發(fā)現(xiàn)大部分功能可能你已經(jīng)熟悉他的概念了。他主要允許你使用一些標(biāo)準(zhǔn)來查找公司的某位職員。一旦當(dāng)你發(fā)現(xiàn)了某個(gè)適當(dāng)?shù)厝耍憔涂梢愿M(jìn)一步的查看他的信息,像電話號碼、辦公桌位置、他的經(jīng)理是誰、他的同事有那些等等。圖1的草圖描繪了這個(gè)電話簿應(yīng)用程序的基本需求和頁面流程。

t_phoneBookAppSketch.jpg

圖 1. 實(shí)例概覽

就像草圖描述的那樣,應(yīng)用程序?qū)嶋H有兩個(gè)模塊組成: Search 模塊允許我們查找一個(gè)我們要的人,而 Detail 模塊則顯示查找到這個(gè)人的詳細(xì)信息。Search 模塊將會使用 Detail 模塊來顯示查詢結(jié)果中某人的詳細(xì)信息。草圖同樣顯示了我們可以在明細(xì)頁直接訪問被查人同事的明細(xì)。這意味著 Detail 模塊可以遞歸使用 Detail 模塊來顯示同事的明細(xì)。

文章稍后,將看到我們可以在一個(gè)單獨(dú)的 Web 流程中定義各個(gè)模塊。這意味著我們會有兩個(gè)流程:一個(gè) Search 流程和一個(gè) Detail 流程。

因?yàn)楸疚牡慕裹c(diǎn)是實(shí)現(xiàn)應(yīng)用程序的 Web 接口,所以我們將會提供包含了硬編碼啞數(shù)據(jù)的基本業(yè)務(wù)層。領(lǐng)域?qū)ο蟊话赾om.ervacon.springframework.samples.phonebook.domain 包中。我們有 4 個(gè)業(yè)務(wù)類:

  • Person         — 一個(gè)簡單的 JavaBean 包含人員的信息 (名, 姓, 電話號碼 ...)。Person 對象使用 User Id 來唯一識別,是 UserId 類的一個(gè)實(shí)例。一個(gè) Person 類同樣也維護(hù)他同事們的引用,同樣都是 Person 類的實(shí)例。
  • UserId         — 這是一個(gè)主要對象用來識別一個(gè) Person。
  • PhoneBookQuery — 一個(gè)查詢對象,在電話簿中描述一個(gè)查詢。我們可以是使用姓,名或者姓名來查詢。
  • PhoneBook      — 我們的主要業(yè)務(wù)門面(Business Facade)。這個(gè)類僅僅定義了一些啞數(shù)據(jù)和兩個(gè)業(yè)務(wù)方法:
    •    public List query(PhoneBookQuery query)
    •    public Person getPerson(UserId userId)

確定好業(yè)務(wù)功能性后,我們準(zhǔn)備使用 Spring Web Flow 來為電話簿應(yīng)用程序開發(fā) Web 接口。

設(shè)置 Spring MVC

在我們開始使用 Web Flow 之前,我們需要配置一個(gè)基本的 Spring Web 應(yīng)用程序。要做的第一件事是我們必須確定在 /WEB-INF/lib 目錄下有我們所要的 jar 文件。一個(gè) Spring Web Flow 的應(yīng)用程序需要4個(gè)jar在類路進(jìn)下: 包含 Spring 框架自身的 Spring.jar;包含 Web Flow 控制器實(shí)現(xiàn)的 webflow.jar; 記錄日志所需的 commons-logging.jar 和用來讀取和分析 Web Flow XML 文件的 jdom.jar;

由于這將是一個(gè)標(biāo)準(zhǔn)的 J2EE Web 應(yīng)用程序,所以需要在 /WEB-INF 目錄下有一個(gè) web.xml 部署文件。下面是此部署描述文件的代碼,它描述了如下事物:

  • contextLoader servlet,當(dāng) Servlet 引擎(如 Tomcat )在加載我們的 Web 應(yīng)用程序的時(shí)候進(jìn)行初始化。
  • 一個(gè)名為 phoneBook 的 Spring Dispatcher Servlet。這個(gè) Servlet 配置了處理所有相匹配的請求 (如 /phoneBook/*)。
  • index.jsp 將會成為應(yīng)用程序的歡迎頁面。
  • 實(shí)例的 JSP 頁面使用到了 Spring 的 標(biāo)簽庫,所以我們需要聲明它。實(shí)際的 TLD 文件存儲在 /WEB-INF/tld 目錄下。
 1xml version="1.0" encoding="UTF-8"?>
 2DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 3    "http://java.sun.com/dtd/web-app_2_3.dtd">
 4<web-app id="WebApp">
 5    <servlet>
 6        <servlet-name>contextLoaderservlet-name>
 7        <servlet-class>org.springframework.web.context.ContextLoaderServletservlet-class>
 8        <load-on-startup>1load-on-startup>
 9    servlet>
10    <servlet>
11        <servlet-name>phoneBookservlet-name>
12        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
13        <load-on-startup>2load-on-startup>
14    servlet>
15    <servlet-mapping>
16        <servlet-name>phoneBookservlet-name>
17        <url-pattern>/phoneBook/*url-pattern>
18    servlet-mapping>
19    <welcome-file-list>
20        <welcome-file>index.jspwelcome-file>
21    welcome-file-list>
22    <taglib>
23        <taglib-uri>/WEB-INF/tld/spring.tldtaglib-uri>
24        <taglib-location>/WEB-INF/tld/spring.tldtaglib-location>
25    taglib>
26web-app>

在 Spring MVC 應(yīng)用程序中,我們需要一個(gè) Spring 應(yīng)用程序上下文。它是你在應(yīng)用程序上下文中定義業(yè)務(wù)對象最好的地方。這樣你就能夠干凈的區(qū)分你的業(yè)務(wù)對象和任何 Web 應(yīng)用工件(artifacts)。讓我們跟隨這個(gè)練習(xí)創(chuàng)建一個(gè) /WEB-INF/applicationContext.xml 文件來定義我們的業(yè)務(wù)門面:phoneBook Bean。

1xml version="1.0" encoding="UTF-8"?>
2DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
3    "http://www.springframework.org/dtd/spring-beans.dtd">
4<beans>
5    <bean id="phoneBook" class="com.ervacon.springframework.samples.phonebook.domain.PhoneBook">
6    bean>
7beans>

我們需要在 web.xml 中適當(dāng)?shù)亩x Dispatcher Servlet。 這個(gè) Servlet 將會以默認(rèn)得方式讀取 /WEB-INF/ServletName-servlet.xml 配置文件。在我們的例子中將會是 /WEB-INF/phoneBook.xml。我們唯一需要配置的事是一個(gè) View Resolver。這個(gè) View Resolver 負(fù)責(zé)將視圖名(如:"criteria")解析成真實(shí)的視圖路進(jìn)(如:/WEB-INF/JSP/criteria.jsp)。下面這段使用的是 InternalResourceViewResolver。 所以在我們的實(shí)例中,所有頁面的位置將會在 /WEB-INF/jsp 目錄中。

我們假設(shè)所有其他的 Dispatcher Servlet 配置為默認(rèn)值。這意味著我們將使用一個(gè)簡單的 BeanNameUrlHandlerMapping 來定位將要處理請求的控制器。對于如何配置 Dispatcher Servlet 的詳細(xì)信息請參考 Spring 的參考文檔(查看資源)。

1xml version="1.0" encoding="UTF-8"?>
2DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
3    "http://www.springframework.org/dtd/spring-beans.dtd">
4<beans>
5    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
6        <property name="prefix"><value>/WEB-INF/jsp/value>property>
7        <property name="suffix"><value>.jspvalue>property>
8    bean>
9beans>


待續(xù)……
 

參考資源: