一个BPMN流程示例带你认识项目中流程的生命周期
摘要:本文详细说明了在工作流Activiti框架中的BPMN流程定义整个运行的生命周期。
本文分享自华为云社区《本文详细说明了在工作流Activiti框架中的BPMN流程定义整个运行的生命周期》,作者:攻城狮Chova。
BPMN 2.0介绍
- 业务流程模型注解(BusinessProcess Modeling Notation - BPMN)是业务流程模型的一种标准图形注解.这个标准是由对象管理组(Object Management Group - OMG)维护的
- BPMN规范的2.0版本允许添加精确的技术细节在BPMN的图形和元素中,同时制定BPMN元素的执行语法.通过使用XML语言来指定业务流程的可执行语法,BPMN规范已经演变为业务流程的语言,可以执行在任何兼容BPMN2的流程引擎中,同时依然可以使用强大的图形注解
- 简单来说,BPMN即图标与标签的结合
定义一个流程
- 创建一个新的XML文件并命名,确认文件后缀为 .bpmn20.xml或 .bpmn, 否则引擎无法发布
- BPMN 2.0根节点是definitions节点. 这个元素中,可以定义多个流程定义(不过建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)
- 一个空的流程定义如下所示:注意definitions元素最少也要包含xmlns和 targetNamespace的声明
- targetNamespace可以是任意值,它用来对流程实例进行分类
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples"> <process id="myProcess" name="My First Process"> .. </process> </definitions>
- 可以选择添加线上的BPMN 2.0格式位置:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd
- ==process元素有两个属性:==
- id: 这个属性是必须的,对应着Activiti ProcessDefinition对象的key属性.id可以用来启动流程定义的流程实例,通过RuntimeService的startProcessInstanceByKey方法
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
==注意:== 它和startProcessInstanceById方法不同:这个方法期望使用Activiti引擎在发布时自动生成的id.可以通过调用processDefinition.getId() 方法获得这个值,生成的id的格式为 key:version, 最大长度限制为64个字符, 如果在启动时抛出了一个ActivitiException: 说明生成的id太长了,需要限制流程的key的长度
- name: 这个属性是可选的, 对应ProcessDefinition的name属性.引擎自己不会使用这个属性,是用来在用户接口显示便于阅读的名称
BPMN流程示例前提
- 已经安装Activiti并且能够运行Activiti Demo
- 使用了独立运行的H2服务器
- 修改db.properties,设置其中的jdbc.url=jdbc:h2:tcp://localhost/activiti,然后启动独立服务器
目标
- 学习Activiti和一些基本的BPMN 2.0概念
- 最终结果是一个简单的Java SE程序可以发布流程定义,通过Activiti引擎API操作流程
- 使用一些Activiti相关的工具,构建自己的业务流程web应用
用例
- 每个月都要给公司领导一个金融报表,由会计部门负责
- 当报表完成时,一个上级领导需要审批文档,然后才能发给所有领导
流程图
- 流程的图形化BPMN 2.0标记:
空开始事件(左侧圆圈),后面是两个用户任务:制作月度财报和验证月度财报,最后是空结束事件(右侧粗线圆圈)
XML内容
- 在业务流程的XML中很容易找到流程的主要元素:
- (空)开始事件是流程的入口
- 用户任务是流程中与操作者相关的任务声明:
- 第一个任务分配给accountancy组
- 第二个任务分配给management组
- 当流程达到空结束事件就会结束
- 这些元素都使用连线连接,这些连线拥有source和target属性,定义了连线的方向
<definitions id="definitions" targetNamespace="http://activiti.org/bpmn20" xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"> <process id="financialReport" name="Monthly financial report reminder process"> <startEvent id="theStart" /> <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' /> <userTask id="writeReportTask" name="Write monthly financial report" > <documentation> Write monthly financial report for publication to shareholders. </documentation> <potentialOwner> <resourceAssignmentExpression> <formalExpression>accountancy</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' /> <userTask id="verifyReportTask" name="Verify monthly financial report" > <documentation> Verify monthly financial report composed by the accountancy department. This financial report is going to be sent to all the company shareholders. </documentation> <potentialOwner> <resourceAssignmentExpression> <formalExpression>management</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' /> <endEvent id="theEnd" /> </process> </definitions>
启动一个流程实例
- 创建好业务流程的流程定义,就可以创建流程实例
- 一个流程实例对应了特定月度财报的创建和审批,所有流程实例都共享同一个流程定义
- 为了使用流程定义创建流程实例,首先要发布业务流程:
- 流程定义会保存到持久化的数据存储里,是为Activiti引擎特别配置的.所以部署好业务流程,在引擎重启后还能找到流程定义
- BPMN 2.0流程文件会解析成内存对象模型, 可以通过Activiti API操作
- 通过下面的API发布流程,所有与Activiti引擎的交互都是通过services
Deployment deployment = repositoryService.createDeployment() .addClasspathResource("FinancialReportProcess.bpmn20.xml") .deploy();
- 启动一个新流程实例,使用我们定义在流程定义里的id(对应XML文件中的process元素).==注意这里的id对于Activiti来说,应该叫做key==,一般在流程模型中使用的ID,在Activiti中都是Key:比如任务ID
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");
- 这样创建一个流程实例:
- 首先进入开始事件
- 开始事件之后,它会沿着所有的外出连线执行,到达第一个任务(“制作月度财报”)
- Activiti会把一个任务保存到数据库里.这时,分配到这个任务的用户或群组会被解析,也会保存到数据库里
- 需要注意,Activiti引擎会继续执行流程的环节,除非遇到一个 等待状态:比如用户任务
- 在等待状态下,当前的流程实例的状态会保存到数据库中.直到用户决定完成任务才能改变这个状态
- 这时,引擎会继续执行,直到遇到下一个等待状态,或流程结束
- 如果中间引擎重启或崩溃,流程状态也会安全的保存在数据库里
- 任务创建之后,startProcessInstanceByKey会在到达用户任务这个等待状态之后才会返回.这时,任务分配给了一个组,这意味着这个组是执行这个任务的候选组
- 现在将所有东西都放在一起,来创建一个简单的java程序:
- 创建一个Java项目,把Activiti的jar和依赖放到classpath下:这些都可以在Activiti发布包的libs目录下找到
- 在调用Activiti服务之前,我们必须构造一个ProcessEngine,可以让我们访问服务
- 这里我们使用[单独运行]的配置,这会使用demo安装时的数据库来构建ProcessEngine
public static void main(String[] args) { // Create Activiti process engine ProcessEngine processEngine = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .buildProcessEngine(); // Get Activiti services RepositoryService repositoryService = processEngine.getRepositoryService(); RuntimeService runtimeService = processEngine.getRuntimeService(); // Deploy the process definition repositoryService.createDeployment() .addClasspathResource("FinancialReportProcess.bpmn20.xml") .deploy(); // Start a process instance runtimeService.startProcessInstanceByKey("financialReport"); }
任务列表
- 可以通过TaskService来获得任务,添加以下逻辑:
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();
- 注意传入的用户必须是accountancy组的一个成员,要和流程定义中相对应:
<potentialOwner> <resourceAssignmentExpression> <formalExpression>accountancy</formalExpression> </resourceAssignmentExpression> </potentialOwner>
- 也可以使用群组名称,通过任务查询API来获得相关的结果.在代码中添加如下逻辑:
TaskService taskService = processEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
- 因为配置的ProcessEngine使用了与demo相同的数据,可以登录到Activiti Explorer.默认,accountancy(会计)组里没有任何人:
- 登录
- 点击组
- 创建一个新组
- 点击用户
- 把组分配给fozzie
- 使用fozzie/fozzie登录
- 就可以启动我们的业务流程了,选择Processes页,在[月度财报]的[操作]列点击[启动流程]
- 流程会执行到第一个用户任务.因为我们以kermit登录,在启动流程实例之后,就可以看到有了一个新的待领任务.选择任务页来查看这条新任务.注意即使流程被其他人启动,任务还是会被会计组里的所有人作为一个候选任务看到
领取任务
- 现在一个会计要认领这个任务
- 认领以后,这个用户就会成为任务的执行人,任务会从会计组的其他成员的任务列表中消失.认领任务的代码:
taskService.claim(task.getId(), "fozzie");
- 任务会进入认领任务人的个人任务列表:
List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
- 在Activiti Explorer UI中,点击认领按钮,会执行相同的操作.任务会移动到登录用户的个人任务列表.你也会看到任务的执行人已经变成当前登陆的用户:
完成任务
- 现在会计可以开始进行财报的工作
- 报告完成后,他可以完成任务,意味着任务所需的所有工作都完成
taskService.complete(task.getId());
- 对于Activiti引擎:
- 需要一个外部信息来让流程实例继续执行
- 任务会把自己从运行库中删除
- 流程会沿着单独一个外出连线执行,移动到第二个任务(审批报告)
- 与第一个任务相同的机制会使用到第二个任务上,不同的是任务是分配给management组
- 在demo中:
- 完成任务是通过点击任务列表中的完成按钮
- 因为Fozzie不是会计,我们先从Activiti Explorer注销
- 然后使用kermit登陆(经理),第二个任务会进入未分配任务列表
结束流程
- 审批任务像之前一样查询和领取.
- 完成第二个任务会让流程执行到结束事件,就会结束流程实例
- 流程实例和所有相关的运行数据都会从数据库中删除
- 登录Activiti Explorer就可以进行验证,可以看到保存流程运行数据的表中已经没有数据:
- 可以使用historyService判断流程是否已经结束:
HistoryService historyService = processEngine.getHistoryService(); HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult(); System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
源码
- 考虑到你可能会在Activiti Explorer UI中启动一些流程实例,这样,它会获得多个任务,而不是一个,所以代码可以一直正常运行:
public class TenMinuteTutorial { public static void main(String[] args) { // Create Activiti process engine ProcessEngine processEngine = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .buildProcessEngine(); // Get Activiti services RepositoryService repositoryService = processEngine.getRepositoryService(); RuntimeService runtimeService = processEngine.getRuntimeService(); // Deploy the process definition repositoryService.createDeployment() .addClasspathResource("FinancialReportProcess.bpmn20.xml") .deploy(); // Start a process instance String procId = runtimeService.startProcessInstanceByKey("financialReport").getId(); // Get the first task TaskService taskService = processEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list(); for (Task task : tasks) { System.out.println("Following task is available for accountancy group: " + task.getName()); // claim it taskService.claim(task.getId(), "fozzie"); } // Verify Fozzie can now retrieve the task tasks = taskService.createTaskQuery().taskAssignee("fozzie").list(); for (Task task : tasks) { System.out.println("Task for fozzie: " + task.getName()); // Complete the task taskService.complete(task.getId()); } System.out.println("Number of tasks for fozzie: " + taskService.createTaskQuery().taskAssignee("fozzie").count()); // Retrieve and claim the second task tasks = taskService.createTaskQuery().taskCandidateGroup("management").list(); for (Task task : tasks) { System.out.println("Following task is available for accountancy group: " + task.getName()); taskService.claim(task.getId(), "kermit"); } // Completing the second task ends the process for (Task task : tasks) { taskService.complete(task.getId()); } // verify that the process is actually finished HistoryService historyService = processEngine.getHistoryService(); HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult(); System.out.println("Process instance end time: " + historicProcessInstance.getEndTime()); } }
总结
- 可以通过Activiti中的BPMN 2.0结构,对业务流程进行以下方面的:
- 定义网关来实现决策环节: 经理可以驳回财报,重新给会计创建一个任务
- 考虑使用变量: 可以保存或引用报告,把它显示到表单中
- 在流程最后加入服务任务: 把报告发给每个领导

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
阿里云开源业内首个应用多活项目 AppActive,与社区共建云原生容灾标准
作者:中西(github @zhongxig),AppActive 负责人,来自阿里云云原生高可用架构团队,从事容灾架构和故障快恢的研发和开源工作。 摘要: 继高可用架构团队的 Sentinel、Chaosblade 开源后,第三个重磅高可用产品:应用多活 AppActive 正式开源,形成高可用的三架马车,帮助企业构建稳定可靠的企业级生产系统,提高企业面对容灾、容错、容量等问题的稳态系统建设能力。 1 月 11 日,在上海的云原生实战峰会上,阿里云智能研究员丁宇发布了“应用多活技术白皮书”,同时为了推动业界容灾的发展,建立云原生业务容灾标准,阿里云对外开源“应用多活”中间件:AppActive。 什么是 AppActive “业务大规模扩展机房资源不可用怎么办?机房挂了怎么办?业务突然奔溃怎么办?台风地震导致断电怎么办?” 2013 年,当时淘宝完成去 O 没多久,双十一的规模较上年进一步飞增。阿里的工程师正面临着上述的这一系列问题,一方面是机房资源非常紧张,容量不足,另一方面是杭州出现罕见的高温天气,机房面临断电的风险。异地多活架构在这个背景下孵化出来,它的载体是集团版本的 Uni...
- 下一篇
许啸宇:从内部研发到开源开发之路|OneFlow U
许啸宇,一流科技研发工程师,现主要从事框架开发工作。2017年,从北京邮电大学硕士毕业后,他去微软亚洲研究院实习,并在当时的导师袁进辉手下接触了系统研发工作。这段经历为他后来的工作埋下了伏笔。 毕业后,他先在腾讯做了一段视频推荐算法工作,而后转向了推荐系统开发。工作两年多,他开始想进入一个成长期的技术领域,在与OneFlow创始人袁进辉的交流后,于2020年7月成为OneFlow框架的开发者。 本文为许啸宇自述。 1 从算法工程师转向系统开发 2016年底,我在微软亚洲研究院跟袁老师前后做了大概三个月的实习。在那里,我第一次接触了比较基础的大型系统研发的工作,对它的结构和设计窥见了一眼。相对于“怎么实现一个功能”,袁老师更关注“最优的设计应该是什么样的”,这个对我影响很大。 正式毕业后,我选择离开北京到了深圳工作,在腾讯做视频推荐工作。记得刚进公司时,有个事业群的推荐算法比赛,其他小伙伴搞特征很厉害,我琢磨了一下不用公司训练平台的XGBoost,而是拿内存占用少、速度快的LightGBM自己搭训练工具。这样单机我们能用更快速度、更多数据去跑任务,小伙伴的特征工程配合这个工具,我们只...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- 设置Eclipse缩进为4个空格,增强代码规范
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS关闭SELinux安全模块
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案