spring 工作流引擎

几个月前,在处理一个公司项目时,我们需要开发REST服务,该服务用于根据客户端应用程序发送的数据发送电子邮件。 在开发此服务期间,我们决定创建简单的工作流引擎,该引擎将为发送电子邮件收费,但该引擎也可用于任何类型的简单流。

在本文中,我将逐步说明如何实现可处理序列流的简单工作流引擎。

为了实现此工作流引擎,我们使用了spring框架,但是无论使用哪种框架,也可以不使用任何框架,如何在任何框架上实现该想法都应相同。

我们将从对序列工作流程模式的简短介绍开始,然后,我们将研究所需的接口,最后,我们将从使用Spring实现工作流程引擎开始。

序列工作流程模式

序列工作流程模式描述了其中每个步骤(动作)一步一步地完成的工作流程。 在下一张图片上,您可以看到它的外观:

流中将要处理的每个动作都共享相同的上下文,这使流的参与者之间可以共享信息。 使用公共上下文的想法是因为每个步骤都应该彼此独立,并且应该将它们作为其他流程的一部分轻松添加。

如果要获取有关序列工作流程模式的更多信息,请访问: 序列模式 。

定义所需的界面

下一步是创建一组接口,使我们可以轻松创建工作流程并定义工作流程操作。

我们可以从Workflow界面开始。 该接口负责处理工作流程操作,实际上它定义了我们的工作流程引擎应该执行的操作。 这是一个非常简单的界面,只有一种方法“ processWorkflow”。

此方法由工作流引擎调用,用于为工作流提供可在工作流内部使用的初始对象,它表示每个工作流的起点。

package ba.codecentric.workflow;import java.util.Map;/*** Process email workflow.** @author igor.madjeric**/public interface Workflow {/*** Method for processing workflow.** @param parameters* maps of object which are needed for workflow processing* @return true in case that workflow is done without errors otherwise false*/public boolean processWorkflow(Map<String, Object> parameters);}Next what we need is interface used for defining workflow action. This is also simple interface whit only one method too.package ba.codecentric.workflow;
/*** Define workflow action** @author igor.madjeric**/public interface WorkflowAction {/*** Execute action.** @param context* @throws Exception*/public void doAction(Context context) throws Exception;}So this interface define only doAction method which will be called by workflow implementation.Last interface which we need to define is Context interface. This interface define two methods, one for setting object in context and another for retrieving it.package ba.codecentric.workflow;/*** Context interface.** Class which extend this interface should be able to provide mechanism for keeping object in context.<br />* So they can be shared between action inside workflow.** @author igor.madjeric**/public interface Context {/*** Set value with specified name in context.* If value already exist it should overwrite value with new one.** @param name of attribute* @param value which should be stored for specified name*/public void setAttribute(String name, Object value);/*** Retrieve object with specified name from context,* if object does not exists in context it will return null.** @param name of attribute which need to be returned* @return Object from context or null if there is no value assigned to specified name*/public Object getAttribute(String name);}

这是我们需要为简单工作流程定义的所有接口

实施简单的工作流引擎

定义接口之后,我们可以从实现工作流引擎开始。 引擎应具备的功能有一些要求。

该引擎应支持顺序工作流程,这意味着一个接一个地执行动作。

发动机也应该能够进动多于一个的流量。

工作流操作应该能够彼此共享信息。

如我们所见,并没有很多要求,所以我们应该从实现它开始。

首先,我们可以创建上下文类,该上下文类将用于处理动作之间的信息。 此类实现Context接口,并且不执行其他任何操作。

package ba.codecentric.workflow.impl;import java.util.HashMap;
import java.util.Map;
import ba.codecentric.workflow.Context;/**
* Save states between different workflow action.
*
* @author igor.madjeric
*
*/
public class StandardContext implements Context {private Map<String, Object> context;/*** Create context object based.
*
* @param parameters
*/
public StandardContext(Map<String, Object> parameters) {
if (parameters == null) {
this.context = new HashMap<String, Object>();
} else {
this.context = parameters;
}
}@Override
public Object getAttribute(String name) {
return context.get(name);
}@Override
public void setAttribute(String name, Object value) {
context.put(name, value);
}}

第二步是创建实现Workflow接口的类。 我们称此类为StandardWorkflow。 除了实现Workflow接口之外,该类还实现了ApplicationContextAware接口,因为需要访问spring bean存储库。 如果您不使用spring,则不需要实现它。

我们已经说过,工作流应该支持一个以上的流。
因此,可以将一个工作流程的操作定义为一个列表,并且每个列表都应分配一个逻辑名称。 因此,对于动作注册,我们可以使用Map <String,List <WorkflowAction >>之类的东西。 首先,我们将看到SpringBean的StandardWorkflow和一个自定义流程的定义,然后我们将看到StandardWorkflow的实现。

Bean的StandardWorkflow定义:

<bean id='standardWorkflow'class='de.codecentric.oev.external.services.workflow.standard.StandardWorkflow'><property name='workflowActions'><map><!-- <entry key='<CID>_action'><ref bean='<CID>_action'/></entry>--><!-- OEVBS --><entry key='action1_action'><ref bean='action1_action' /></entry><!-- PVN --><entry key='action2_action'><ref bean='action2_action' /></entry><!-- WPV --><entry key='action3_action'><ref bean='action3_action' /></entry></map></property></bean>

从这个bean定义中,我们可以看到我们为每个客户定义了操作,并且在引用bean中定义了操作列表。

这是其中一个客户Bean的示例:

<bean id='action1_action' class='java.util.ArrayList'><constructor-arg><!-- List of Actions --><list value-type='ba.codecentric.workflow.WorkflowAction' ><ref local='createEmailAction'/><ref bean='sendEmailAction'/></list></constructor-arg></bean>

现在我们可以看到StandardWorkflow的样子:

package ba.codecentric.workflow.impl;import java.util.List;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import ba.codecentric.workflow.Context;import ba.codecentric.workflow.Workflow;import ba.codecentric.workflow.WorkflowAction;/*** Define standard workflow for sending email.** @see Workflow** @author igor.madjeric**/public class StandardWorkflow implements Workflow,ApplicationContextAware {private final Log LOG = LogFactory.getLog(StandardWorkflow.class);private static final String ACTION = 'action';private Map<String, List<WorkflowAction>> workflowActions;private ApplicationContext applicationContext;/***@see de.codecentric.oev.external.services.workflow.Workflow#processWorkflow(java.util.Map)*/@Overridepublic boolean processWorkflow(String workflofName, Map<String, Object> parameters) {Context context = new StandardContext(parameters);List<WorkflowAction> actions = getWorkflowActions(workflofName);for (WorkflowAction action : actions) {try {action.doAction(context);} catch (Exception e) {StringBuilder message = new StringBuilder(
'Failed to complete action:' + action.toString());message.append('\n');message.append(e.getMessage());LOG.error(message.toString());return false;}}return true;}
private List<WorkflowAction> getWorkflowActions(String actionName) {List<WorkflowAction> actions = workflowActions.get(actionName);if (actions == null || actions.isEmpty()) {LOG.error('There is no defined action for ' + actionName);throw new IllegalArgumentException(
'There is no defined action for ' + actionName);}return actions;}
@Overridepublic void setApplicationContext(ApplicationContext applicationContext)
throws BeansException{
this.applicationContext = applicationContext;}
// Getter/Setterpublic Map<String, List<WorkflowAction>> getWorkflowActions() {return workflowActions;}
public void setWorkflowActions(Map<String, List<WorkflowAction>> workflowActions) {this.workflowActions = workflowActions;}
}

再次您可以看到,这也是一个简单的类,所有工作都在processWorkflow方法中完成,我们向其提供流程名称和输入参数。 此方法使用指定的参数创建Context,然后尝试加载为指定流定义的操作,如果存在具有指定名称的流,它将开始运行流。

如何开始流程

这取决于您的需要。 您可以使用我们这样的休息服务,也可以使用其他任何机制(例如MBean),计划的作业,也可以直接从某些服务中进行呼叫。 您需要做的就是调用processWorkflow方法。

参考:来自ICG Madjeric博客的JCG合作伙伴 Igor Madjeric的《 Spring的简单工作流引擎》 。

翻译自: https://www.javacodegeeks.com/2012/11/simple-workflow-engine-with-spring.html

spring 工作流引擎

spring 工作流引擎_带Spring的简单工作流引擎相关推荐

  1. spring boot测试_测试Spring Boot有条件的合理方式

    spring boot测试 如果您或多或少有经验的Spring Boot用户,那么很幸运,在某些时候您可能需要遇到必须有条件地注入特定bean或配置的情况 . 它的机制是很好理解的 ,但有时这样的测试 ...

  2. spring 事务 会话_测试Spring的“会话”范围

    spring 事务 会话 在基于Spring的Web应用程序中,bean的作用域可以是用户"会话". 从本质上讲,这意味着对会话范围Bean的状态更改仅在用户会话范围内可见. 本条 ...

  3. spring mysql 注解_【Spring】SpringMVC之基于注解的实现SpringMVC+MySQL

    目录结构: contents structure [-] SpringMVC是什么 MVC的全称是Model View Controller,通过实现MVC框架可以很好的数据.视图.业务逻辑进行分离. ...

  4. spring生命周期_理解Spring应用上下文生命周期

    Spring应用上下文启动准备阶段 `BeanFactory`创建阶段 `BeanFactory`准备阶段 `BeanFactory`后置处理阶段 `AnnotationConfigServletWe ...

  5. java spring 条件注解_【Spring】Spring高级话题-条件注解-@Condition

    进行本示例的演示,需要先配置好Maven和Spring哦. 见: [Spring]基于IntelliJ IDEA搭建Maven 分析 通过profile,我们可以获得不同的profile,我们可以获得 ...

  6. java oa工作流设计_简易OA漫谈之工作流设计(DB)

    1.流程图. 工作流可以做得很复杂,也可以设计的很简单.看下图 看这个图,一个流程图最基础的三部分:流程,步骤,操作. 2.流程模板. 流程图的程序描述就叫流程模板.一个流程模板大概需要的一些属性如下 ...

  7. mysql怎么换引擎_如何更换MySQL默认存储引擎

    查看MySQL默认存储引擎 登录MySQL服务器,运行show engines命令: mysql> show engines; +------------+---------+--------- ...

  8. ioc spring 上机案例_抛开Spring去理解IOC思想 - 原来IOC容器这么简单

    很多小伙伴们看到标题可能就会想到抛开Spring就不会存在IOC思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解IOC的时候通常会和Spring放到一起去学习,首先呢Spring设计的非 ...

  9. idea gwt 怎么编译_带Spring Boot的GWT

    idea gwt 怎么编译 介绍 我最近一直在研究用Java编写UI代码的选项. 在我以前的文章中,我研究了Eclipse RAP,发现它可以与Spring Boot集成在一个可执行jar中. 这次我 ...

最新文章

  1. 鹅厂2020暑期实习第一次一面
  2. 记录自己最近犯得一些傻事
  3. 内存管理:_CrtDumpMemoryLeaks和_CrtSetBreakAlloc
  4. python训练词库_在Python中训练NGramModel
  5. redis怎么确认启动成功_不懂分Redis?现在开始动手!
  6. mysql中游标能不能更新数据库_数据库游标更新数据
  7. tomcat中三种部署项目的方法(转)
  8. 微信开发之(四)获取关注后的图文推送信息
  9. 学python需要什么基础-学习Python需要哪些基础知识?
  10. bootice安装grub2-00 到硬盘或者U盘mbr
  11. 一见钟情 歌词 翻译
  12. 易语言-通过快捷方式.lnk文件获取.exe源文件地址
  13. eclipse 上传代码到github,我花费了一个月测的。就这样了
  14. 曾经觉得学习晦涩难懂的我是如何爱上linux
  15. ZN-1AI工业机器人与智能视觉系统应用实训平台
  16. 通过私有化部署自建一套视频流媒体服务器平台,如何解决视频播放延时卡顿问题?
  17. 三节课P1课程思维导图
  18. 如何理解路由器的包转发率
  19. 操作系统——生产者消费者模型以及信号量
  20. 孔雀石SDR入手体验

热门文章

  1. Spring Boot 单元测试详解+实战教程
  2. 用python画出吉祥物
  3. AntDesignPro一次添加多条数据的表单字数限制,并且把input框变为可变文本框
  4. JS中函数的prototype属性和对象的__proto__属性
  5. css 图片换行_好程序员web前端学习路线分享CSS浮动-文档流篇
  6. pre2-flink单机部署与job提交
  7. 转:认识cpu、核与线程
  8. Spring boot(九):定时任务
  9. redis nosql_NoSql数据库:Cassandra,Mongo,Redis数据库比较
  10. gradle跟pom_Gradle多项目构建–父pom式结构