Spring Web Flows

實(shí)踐指南

原文:Spring Web Flows - A Practical Guide

Author: Erwin Vervaet (Mail To)

translator: Dorian Shi (Mail To)

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

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

介 紹

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

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

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

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

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

 

熟悉業(yè)務(wù)過(guò)程管理(BPM) 的讀者將認(rèn)識(shí)到 Web 流程是普通工作流的一個(gè)特例,所以他們?cè)诶碚撋峡梢允褂孟?JBMP(請(qǐng)查看資源) 來(lái)實(shí)現(xiàn)一般化的 BMP 系統(tǒng)。既然簡(jiǎn)單是 Spring Web Flow 的重要設(shè)計(jì)目的,所以它不會(huì)去使用這種一般化的工作流引擎。在我們 Web 應(yīng)用中,我們會(huì)用一個(gè)簡(jiǎn)單的 Web 流程來(lái)描述一個(gè)頁(yè)面的流程。

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

 

實(shí) 例

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

t_phoneBookAppSketch.jpg

圖 1. 實(shí)例概覽

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

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

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

  • Person         — 一個(gè)簡(jiǎn)單的 JavaBean 包含人員的信息 (名, 姓, 電話號(hào)碼 ...)。Person 對(duì)象使用 User Id 來(lái)唯一識(shí)別,是 UserId 類的一個(gè)實(shí)例。一個(gè) Person 類同樣也維護(hù)他同事們的引用,同樣都是 Person 類的實(shí)例。
  • UserId         — 這是一個(gè)主要對(duì)象用來(lái)識(shí)別一個(gè) Person。
  • PhoneBookQuery — 一個(gè)查詢對(duì)象,在電話簿中描述一個(gè)查詢。我們可以是使用姓,名或者姓名來(lái)查詢。
  • 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 來(lái)為電話簿應(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 和用來(lái)讀取和分析 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 配置了處理所有相匹配的請(qǐng)求 (如 /phoneBook/*)。
  • index.jsp 將會(huì)成為應(yīng)用程序的歡迎頁(yè)面。
  • 實(shí)例的 JSP 頁(yè)面使用到了 Spring 的 標(biāo)簽庫(kù),所以我們需要聲明它。實(shí)際的 TLD 文件存儲(chǔ)在 /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ù)對(duì)象最好的地方。這樣你就能夠干凈的區(qū)分你的業(yè)務(wù)對(duì)象和任何 Web 應(yīng)用工件(artifacts)。讓我們跟隨這個(gè)練習(xí)創(chuàng)建一個(gè) /WEB-INF/applicationContext.xml 文件來(lái)定義我們的業(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 將會(huì)以默認(rèn)得方式讀取 /WEB-INF/ServletName-servlet.xml 配置文件。在我們的例子中將會(huì)是 /WEB-INF/phoneBook.xml。我們唯一需要配置的事是一個(gè) View Resolver。這個(gè) View Resolver 負(fù)責(zé)將視圖名(如:"criteria")解析成真實(shí)的視圖路進(jìn)(如:/WEB-INF/JSP/criteria.jsp)。下面這段使用的是 InternalResourceViewResolver。 所以在我們的實(shí)例中,所有頁(yè)面的位置將會(huì)在 /WEB-INF/jsp 目錄中。

我們假設(shè)所有其他的 Dispatcher Servlet 配置為默認(rèn)值。這意味著我們將使用一個(gè)簡(jiǎn)單的 BeanNameUrlHandlerMapping 來(lái)定位將要處理請(qǐng)求的控制器。對(duì)于如何配置 Dispatcher Servlet 的詳細(xì)信息請(qǐng)參考 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ù)……
 

參考資源: