Spring Web Flows

实践指南

原文:Spring Web Flows - A Practical Guide

Author: Erwin Vervaet (Mail To)

translator: Dorian Shi (Mail To)

这篇文章介绍了 Spring Web Flows。用实例来说明基于 Web Flow 的原理。文章也提供了一个使用 Web Flow 和 Spring 框架来构建 Web 应用的实践指南.

假定读者已经了解一些 J2EE Web 应用、XML、Spirng 框架,当然还有 Spring 的 Web MVC 框架。你可以查看资源来了解这些信息。

介 绍

在一个 Web 应用程序中传统的定义一个页面流程是一个一点也不直观的过程。像 Struts 和 Spring 这样的框架促使你把页面流程放进单独的控制器和视图中。举个例子:Struts 会把请求映射到一个 Action。这个 Action 会选择一个视图并转发到这个视图。虽然这是一个简单并且实用的方法,但它有一个主要的缺点:到 Action 的定义文件 struts-config.xml 中看看,Web 应用程序的所有页面流程一点也不清晰。因为 Action 不能简单的被重用,灵活性同样也会遭到损害。

Spring Web MVC 框架提供了一个细微的高级功能:表单控制器执行一个预先定义好的工作流。有两种控制器提供了这一特性:SimpleFormController 和 AbstractWizardFormController。然而,这些仍旧是一种一般化的硬编码的工作流概念。

到这里就要引入 Spring 的 Web Flow 了。它允许你用清晰简单的方式展现 Web 应用的页面流程。就像我们要看到的一样,他有许多有点:

  • 一个 Web 应用的页面流程通过 Web Flow 定义文件(一个 XML 文件)清楚的展现出来。
  • Web Flow 可以被设计成自包含的(slef contained)。这意味着允许你在很多情形中把你应用程序的一部分看成一个组件并且使之重用。
  • Web Flow 可以在一个 Web 应用中总是使用一样的手法来定义任何合理的页面流程。你无需在非常特殊的情况下被迫使用专门的控制器。

现在 Web Flow 有足够的能力表示由一系列 State 组成的一个 Web 流程。State 是事件发生的流程点:举例来说就是显示一个视图或者执行一个 Action。每个 State 有一个或多个 transitions ,他们习惯于从一个 State 转到另一个 State 。一个 transitions 被一个 Event 所触发。 为了让你对 Web 流程有一个大概的印象,下面这段 XML 定义了一个 Web 流程,大致等效于实现一个 SimpleFormController 的工作流 。对于这个 Web 流程的原理将在本文的稍后详细说明。

 

熟悉业务过程管理(BPM) 的读者将认识到 Web 流程是普通工作流的一个特例,所以他们在理论上可以使用像 JBMP(请查看资源) 来实现一般化的 BMP 系统。既然简单是 Spring Web Flow 的重要设计目的,所以它不会去使用这种一般化的工作流引擎。在我们 Web 应用中,我们会用一个简单的 Web 流程来描述一个页面的流程。

本文的剩余部分会用一个实例来说明这方面内容,可以使用这个连接来下载源代码:PhoneBook.war.zip 。现在下载并解压缩这个文件可能是个好注意,它能够在你阅读文章的时候帮助你学习。

 

实 例

实例是一个使用 Spring Web Flow 的电话簿应用程序,我们将使用图解的方式来说明他的功能意图。这是一个典型的公司内网的应用程序,你会发现大部分功能可能你已经熟悉他的概念了。他主要允许你使用一些标准来查找公司的某位职员。一旦当你发现了某个适当地人,你就可以更进一步的查看他的信息,像电话号码、办公桌位置、他的经理是谁、他的同事有那些等等。图1的草图描绘了这个电话簿应用程序的基本需求和页面流程。

t_phoneBookAppSketch.jpg

图 1. 实例概览

就像草图描述的那样,应用程序实际有两个模块组成: Search 模块允许我们查找一个我们要的人,而 Detail 模块则显示查找到这个人的详细信息。Search 模块将会使用 Detail 模块来显示查询结果中某人的详细信息。草图同样显示了我们可以在明细页直接访问被查人同事的明细。这意味着 Detail 模块可以递归使用 Detail 模块来显示同事的明细。

文章稍后,将看到我们可以在一个单独的 Web 流程中定义各个模块。这意味着我们会有两个流程:一个 Search 流程和一个 Detail 流程。

因为本文的焦点是实现应用程序的 Web 接口,所以我们将会提供包含了硬编码哑数据的基本业务层。领域对象被包含在com.ervacon.springframework.samples.phonebook.domain 包中。我们有 4 个业务类:

  • Person         — 一个简单的 JavaBean 包含人员的信息 (名, 姓, 电话号码 ...)。Person 对象使用 User Id 来唯一识别,是 UserId 类的一个实例。一个 Person 类同样也维护他同事们的引用,同样都是 Person 类的实例。
  • UserId         — 这是一个主要对象用来识别一个 Person。
  • PhoneBookQuery — 一个查询对象,在电话簿中描述一个查询。我们可以是使用姓,名或者姓名来查询。
  • PhoneBook      — 我们的主要业务门面(Business Facade)。这个类仅仅定义了一些哑数据和两个业务方法:
    •    public List query(PhoneBookQuery query)
    •    public Person getPerson(UserId userId)

确定好业务功能性后,我们准备使用 Spring Web Flow 来为电话簿应用程序开发 Web 接口。

设置 Spring MVC

在我们开始使用 Web Flow 之前,我们需要配置一个基本的 Spring Web 应用程序。要做的第一件事是我们必须确定在 /WEB-INF/lib 目录下有我们所要的 jar 文件。一个 Spring Web Flow 的应用程序需要4个jar在类路进下: 包含 Spring 框架自身的 Spring.jar;包含 Web Flow 控制器实现的 webflow.jar; 记录日志所需的 commons-logging.jar 和用来读取和分析 Web Flow XML 文件的 jdom.jar;

由于这将是一个标准的 J2EE Web 应用程序,所以需要在 /WEB-INF 目录下有一个 web.xml 部署文件。下面是此部署描述文件的代码,它描述了如下事物:

  • contextLoader servlet,当 Servlet 引擎(如 Tomcat )在加载我们的 Web 应用程序的时候进行初始化。
  • 一个名为 phoneBook 的 Spring Dispatcher Servlet。这个 Servlet 配置了处理所有相匹配的请求 (如 /phoneBook/*)。
  • index.jsp 将会成为应用程序的欢迎页面。
  • 实例的 JSP 页面使用到了 Spring 的 标签库,所以我们需要声明它。实际的 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 应用程序中,我们需要一个 Spring 应用程序上下文。它是你在应用程序上下文中定义业务对象最好的地方。这样你就能够干净的区分你的业务对象和任何 Web 应用工件(artifacts)。让我们跟随这个练习创建一个 /WEB-INF/applicationContext.xml 文件来定义我们的业务门面: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 中适当的定义 Dispatcher Servlet。 这个 Servlet 将会以默认得方式读取 /WEB-INF/ServletName-servlet.xml 配置文件。在我们的例子中将会是 /WEB-INF/phoneBook.xml。我们唯一需要配置的事是一个 View Resolver。这个 View Resolver 负责将视图名(如:"criteria")解析成真实的视图路进(如:/WEB-INF/JSP/criteria.jsp)。下面这段使用的是 InternalResourceViewResolver。 所以在我们的实例中,所有页面的位置将会在 /WEB-INF/jsp 目录中。

我们假设所有其他的 Dispatcher Servlet 配置为默认值。这意味着我们将使用一个简单的 BeanNameUrlHandlerMapping 来定位将要处理请求的控制器。对于如何配置 Dispatcher Servlet 的详细信息请参考 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>


待续……
 

参考资源: