摘要 spring是支持控制反转编程机制
个相对新
框架
本文把spring作为简单工作流引擎
将它用在了更加通用
地方
在对工作流简单介绍的后
将要介绍在基本工作流场景中基于Spring
工作流API
使用
许多J2EE应用
要求在
个和主机分离
上下文中执行处理过程
在许多情况下
这些后台
进程执行多个任务
些任务依赖于以前任务
状态
由于这些处理任务的间存在相互依赖
关系
使用
套基于过程
思路方法
常常不能满足要求
开发人员能够利用Spring来容易地将后台进程分离成活动
集合
Spring容器连接这些活动
并将它们组织成简单
工作流
在本文中
简单工作流被定义成不需要用户干预
以
定顺序执行
任意活动
集合
然而
我们并不建议将这种方式代替存在
工作流框架
在
些场景中
需要更多
用户交互
例如基于用户输入而进行
转向
连接或传输
这时
比较好
思路方法是配用
个单独
开源或者商业
工作流引擎
个开源项目已经成功地将更复杂
工作流设计集成到spring中
如果你手上
工作流任务是简单
那么
和功能完备
独立工作流框架相比
简单工作流
策略就会变得有意义
特别地
如果已经使用了spring
这种快速实现可以保证时间不会变得更加漫长
此外
考虑到spring轻量级
控制反转容器
特点
spring在资源负载上减少了资源负载
这篇文章简短地从编程主题
角度介绍工作流
通过使用工作流
概念
spring被用来作为驱动工作流引擎
框架
然后
讨论了生产部署选项
现在
让我们从工作流
设计模式和相关背景信息来介绍简单工作流
思想吧
简单工作流 工作流模型是
个早在70年代就有人开始研究
主题
许多开发者都试图创建工作流模型规范标准
W.H.M. van der Aalst等人写了
工作流模型
白皮书(2003年7月)
它成功地提炼出
组设计模式
这些设计模式准确地将大多数通用
工作流场景建模
当中
最普通
工作流模式是顺序模式 (Sequence pattern)
顺序工作流模式满足了简单工作流
设计原则
并且由
组顺序执行
活动组成
UML(统
建模语言)活动图通常被用来作为
个机制对工作流建模
图1显示了
个基本
使用标准UML活动图对顺序工作流过程
建模过程
JAVA中文站社区门户
zP0M'[7F
图 1顺序工作流模式
)n s
mzLo:nn+A 顺序工作流是
个在J2EE中流行
标准工作流模式
J2EE应用
在后台线程中
通常需要
些顺序发生
事件或者异步事件
图2中
活动图描述了
个简单
工作流
用来通知感兴趣
旅行者
他们感兴趣
目
地
机票价格已经下降
事件
JAVA中文站社区门户3los e'iTnN
图 2.机票价格下降
简单工作流
hl gW:VU3@@ 图1中
航线工作流负责创建和发送动态
email通知
过程中
每
步表示了
个活动(activity)
在工作流处于活动的前
些额外事件必须发生
在这个例子中
事件是飞行路线费率
减少
让我们来简要
看
下航线工作流
业务逻辑
如果第
个活动找不到对费率减少通知感兴趣
用户
那么整个工作流就被取消
如果发现了感兴趣
用户
那么接下来
活动继续执行
随后
个XSL(扩展样式表)转换生成消息内容
的后
记录审计信息 (audit information)
最后
工作流试图通过SMTP服务器发送这个消息
如果这个任务没有
地完成
便在日志中记录成功
信息
进程结束
但是
如果在和SMTP服务器通讯时发生了
个特别
处理例程将要管理这些
处理代码将会试着去重新发送消息
考虑这个航线
例子
个明显
问题是:你如何样有效地将顺序处理过程分解为单独
活动?这个问题被spring巧妙
处理了
下面
让我们快速地讨论spring
反转控制框架
控制反转 Spring通过使用spring容器来负责控制对象的间
依赖关系
使得我们不再对对象的间
依赖负责
这种依赖关系
实现就是大家所知道
控制反转(IoC)或依赖注射
参见Martin Fowler's "Inversion of Control Containers and the Dependency Injection Pattern"(martinfowler.com, 2004年2月)得到有关控制反转和依赖注射
更加深入
讨论
通过管理对象的间
依赖关系
spring就不需要那些只是为了使类能够相互协作
而将对象粘合
代码
作为spring beans
工作流组件 在进
步讨论的前
现在是简要介绍spring中主要概念
恰当时候
接口ApplicationContext是从接口BeanFactory继承
它被用来作为在spring容器内实际
控制实体和容器
ApplicationContext负责对
组作为spring beans
组bean
化
配置和生命期管理
我们通过装配在
个基于XML
配置文件中
spring beans来配置ApplicationContext
这个配置文件介绍说明了spring beans互相协作
本质特点
这样
用spring
术语来说
和其他spring beans交互
spring beans就被叫着协作者(collaborators)
缺省情况下
spring beans是作为单例存在于ApplicationContext中
但是
单例
属性能够被设置为false
从而有效地改变他们在spring中
原型模式时
行为
回到我们
例子
在飞机票价下降
时候
个SMTP发送例程
抽象就被装配在工作流过程例子中
最后
活动(例子代码可以在 Resources中得到)
由于是第5个活动
我们命名它为activity5
要发送消息
activity5就要求
个代理协作者和
个错位处理句柄
=org.iocworkflow.test.sequence.ratedrop.SendMessage id=activity5>
将工作流组件实施成spring beans产生了两个令人喜悦
结果
就是容易进行单元测试和很大程度上可重用能力
IoC容器
特点明显地提供了有效
单元测试
使用像spring这样
Ioc容器
在测试期间
协作者的间
依赖能够容易
用假
替代者替代
在这个航线
例子中
能够容易地从唯
测试ApplicationContext中检索出像activity5活动这样
spring bean
用
个假
SMTP代理SMTP服务器
就有可能单独地测试activity5
第 2个意外
结果
可重用能力是通过像XSL转换这样
工作流活动实现
个被抽象成工作流活动
XSL转换现在能够被任何处理XSL转换
工作流所重用
装配工作流 在提供
API中(从Resources下载)
spring控制了
些操作者以
种工作流
方式交互
关键接口如下: Activity: 封装了工作流中
个单步业务逻辑 ProcessContext:在工作流活动的间传递具有ProcessContext类型
对象
实现了这个接口
对象负责维护对象在工作流转换中从
个活动转换到另
个活动
状态
ErrorHandler: 提供
处理
回调思路方法
Processor: 描述
个作为主工作流线程
执行者
bean
下面从例子源码中摘录
代码是将航线例子装配为简单工作流过程
spring bean
配置
=org.iocworkflow.SequenceProcessor id=rateDropProcessor>
org.iocworkflow.test.sequence.ratedrop.RateDropContext SequenceProcessor类是
个对顺序模式建模
具体子类
有5个活动被连接到工作流处理器
工作流处理器将顺序执行这5个活动
和大多数过程式后台进程相比
工作流
解决方案真正
突出了高度强壮
处理
处理句柄可以单独地处理每个活动
这种类型
句柄在单
活动级别提供了细致
处理
如果没有单独处理单个活动
处理句柄
那么全局工作流处理器
处理句柄将会处理出现
问题
例如
如果在工作流处理过程中
任意时刻
个没有被处理
出现了
那么它将会向外传播
被使用defaultErrorHandler属性装配
ErrorHandler Bean处理
更复杂
工作流框架将工作流转换的间
状态持久化存储到数据库中
在这篇文章中
我们仅仅对状态转换是自动完成
工作流感兴趣
状态信息仅仅在实际工作流运行时在ProcessContext中得到
在ProcessContext中
你仅仅能看到ProcessContext
接口
两个思路方法: public
erface ProcessContext extends Serializable { public boolean stopProcess
; public void
SeedData(Object seedObject); } 用于航线例子工作流
具体
ProcessContext类是RateDropContext类
RateDropContext类封装了用于执行航线费率降低工作流必须
数据
到现在为止
经由缺省
ApplicationContext
作用
所有bean例子都已经成了单例
但是
对于每
个航线工作流
我们必须创建
个新
RateDropContext类
例子
为了处理这种需求
需要配置SequenceProcessor
采用全限定类名作为processContextClass属性
值
对于每个工作流
执行
SequenceProcessor使用指定
类名从spring检索ProcessorContext类
个新
例子
为了使这种机制能够起作用
非单件
spring bean或者类型org.iocworkflow.test.sequence.simple.SimpleContext
原型必须存在于ApplicationContext中
播种工作流 既然我们知道怎样使用spring来组装
个简单
工作流
就让我们集中精力使用种子数据(seed data)举例工作流
过程
要明白怎样开始工作流
看
看在实际接口Processor上表现
思路方法: public
erface Processor { public boolean supports(Activity activity); public void doActivities
; public void doActivities(Object seedData); public void
Activities(List activities); public void
DefaultErrorHandler(ErrorHandler defaultErrorHandler); } 大多数情况下
工作流需要
些
化激活才能开始
开始
个处理过程有两个选项:doActivities(ObjectseedData)思路方法或者无参数
doActivities()
下面
代码列表是包含在样例代码中为SequenceProcessor而实现
doActivities(): public void doActivities(Object seedData) { //Retrieve injected by Spring List activities = getActivities
; //Retrieve a
instance of the Workflow ProcessContext ProcessContext context = createContext
;
(seedData != null) context.
SeedData(seedData); //Execute each activity in sequential order for (Iterator it = activities.iterator
; it.hasNext
;) { Activity activity = (Activity) it.next
; try { context = activity.execute(context); } catch (Throwable th) { //Determine
an error handler is available at the activity level ErrorHandler errorHandler = activity.getErrorHandler
;
(errorHandler
null) { getDefaultErrorHandler
.handleError(context, th);
; }
{ //Handle error using default handler errorHandler.handleError(context, th); } } //Ensure it's ok to continue the process
(processShouldStop(context, activity))
; } } 在这个航空费用减少
例子中
工作流过程
种子数据包括航线信息和费率减少
信息
使用容易测试
航线工作流例子
通过doActivities(Object seedData)思路方法发出种子数据并激活
个单
工作流过程是简单
: BaseProcessor processor = (BaseProcessor)context.getBean("rateDropProcessor"); processor.doActivities(createSeedData
); 这些代码是从包含在这篇文章中
测试例子中摘录
rateDropProcessor Bean是从ApplicationContext中检索来
rateDropProcessor实际上是装配成SequenceProcessor
例子来处理顺序执行
createSeedData()思路方法例子化
个对象
这个对象封装了
化航线工作流所需要
所有种子数据
Processor选项 虽然包含在源代码中
Processor具体
子类仅仅是SequenceProcessor
但是
许多Processor接口
实现也是可以想象得到
可以开发其他工作流处理过程子类来控制区别
工作流类型
例如
另
种像并行切割模式那样有着变化
执行路径
工作流
对于简单工作流来说
活动
顺序是预先决定了
所以SequenceProcessor是好
选择
尽管没有被包括进来
对于使用基于spring
简单工作流
实现来说
排他选择模式是另
个好
选择
当使用排他选择模式时
在每个活动执行的后
Processor具体类就会讯问ProcessorContext
接下来将要执行哪
个活动
注:有关并行切割
排他选择和其他工作流模式
更多信息
请参看W.M.P. van der Aalst等人写
工作流模式
书
启动工作流 考虑到工作流过程常常需要异步执行
特点
使用分离
执行线程来启动工作流就变得有意义了
对于工作流
异步启动而言
有好几个选项;我们主要集中在其中
两个:积极地检测(actively polling)
个队列来启动工作流
或者使用通过ESB(enterprise service bus, 企业服务总线)
事件驱动方式来启动工作流
而Mule就是ESB
个开源项目
图3和图4描绘了两种启动策略
图3中
积极检测在工作流中第
个活动经常检查资源
情形下发生
比如数据源或POP3邮件帐户
如果图3中
积极检测发现有任务等待处理
那么启动就会开始
"mBbz3k:zl kX
图 3. 通过积极检测来启动工作流
JAVA中文站社区门户kp.B%g'@|o._ 另
方面
图4表示了使用JMS(JAVA消息服务)
J2EE应用
把事件放到队列上
情形
个通过ESB配置
事件监听器收到图4中
事件
并且开始工作流
这样
启动工作流过程
JAVA中文站社区门户K6n v#p.q@g
图 4. 通过ESB事件来启动工作流
n|,w6Vn9s_/P-Z 使用所提供样例
代码
让我们更详细
看看主动选择启动方式和事件驱动
启动方式
积极检测 积极检测是
种花费较少
启动工作流过程
方案
SequenceProcessor足够灵活
以使得能够通过平滑
选择工作来进行启动过程
尽管并不令人满意
在没有时间进行事件驱动子系统
配置和部署
许多情景中
积极检测是明智
选择
使用Spring
ScheduledTimerTask
检测模式就能够容易地装配
缺点就是必须创建额外
活动来进行检测
这个检测活动必须被设计来讯问某些实体
如数据库表
pop邮件帐户
或者Web服务
然后决定新
工作是否等待参和到工作流中
在所提供
例子中
PollingTestCase类例子化
个基于检测
工作流过程
使用
个有着积极检测处理过程和事件驱动
启动过程
区别的处在于
spring支持doActivities()思路方法
无参数版本
相反地
在事件驱动
启动中
启动处理过程
实体通过doActivities(Object seedData)思路方法提供了种子数据来启动工作流
检测思路方法
另
个缺点是:资源不
定能够被重复地使用
依赖于应用
环境
这种资源
消耗是不可接受
下面代码例子演示了使用积极检测来控制工作流启动
个活动: public
PollForWork implements Activity { public ProcessContext execute(ProcessContext context) throws Exception { //First check
work needs to be done boolean workIsReady = lookIntoDatabaseForWork
;
(workIsReady) { //The Polling Action must also load any seed data ((MyContext) context).
SeedData(createSeedData
); }
{ //Nothing to do, terminate the workflow process for this iteration ((MyContext) context).
StopEntireProcess(true); }
context; } } 此外
包含在例子代码
单元测试中
PollRates类提供了
个主动选举启动
可以运行
例子
PollRates模拟了对于航线费率下降
重复检查
通过ESB
事件驱动启动工作流 理想地
个包含了适当
种子数据
线程能够异步地启动工作流
这种情况
个例子是收到从JAVA消息服务队列
消息
个监听JMS队列或者主题
客户会收到通知
这个通知告知处理应该在onMessage()思路方法中开始工作流
然后
通过使用Spring和doActivities(Object seedData)思路方法就能够获得工作流处理器Bean
使用ESB
实际用于发送启动事件
机制能够恰当地从工作流处理器中分离出来
开源项目Mule ESB有紧凑地和Spring相集成
好处
任意传送机制
比如JMS
JVM
或者POP3邮箱都能够发起事件
传播
工作流
连续运行 工作流引擎后台进程应该能够没有干扰地连续运行
对于正在运行
基于spring
工作流单
进程来说好
有几个选项
个有着
()思路方法
简单Java类就足够演示和这篇文章伴随着
单元测试中
例子了
个更加可靠
用于部署
机制是嵌入工作流到某种形式
J2EE组件中
Spring很好地支持和J2EE兼容
web应用
归档或者war文件
集成
基于Java管理附件(JMX)服务归档和JBoss应用服务器(更多信息
参见JBoss homepage)支持
sar文件是更加合适
可部署组件
这种更合适
可部署组件也能够被用来将部署归档
在JBoss 4.0中
sar文件已经被大家所知道
deployer
格式所取代了
例子代码 打包成zip格式
例程代码最好是用Apache Maven来使用它们
你能够在主源代码目录src/java找到API
src/java目录中有 3个单元测试
包括:SimpleSequenceTestCase
RateDropTestCase和PoolingTestCase
要运行所有这些测试
在命令行shell中键入maven test
然后在编译和运行的前
Maven将会下载所有必需
jar文件
实际
XSL转换将会发生在两个测试中
它们
结果被管道输出到控制台
键入maven test:ui来拉出图形化
测试运行器
然后选择你想要运行
测试
并且观察控制台
结果
结论 在这篇文章中你已经通过设计模式看到了工作流过程种类
在这些模式中
我们主要集中介绍了顺序模式
通过使用接口
我们来对基本工作流组件建模
通过装配多个接口实现到Spring
实现
个顺序工作流
最后还讨论了启动和部署工作流
区别选项
这里所提出
简单工作流技术肯定不是最终
和革命性
但是
使用Spring来实现像工作流这样
通用任务是
个通过使用IoC容器而获得
效率
好
举例
由于减少了粘合性代码
需要
Spring在保持面向对象
约束同时
减少面向对象操作麻烦
程度
TAG:
Spring
spring
工作流
引擎