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可以用来启动流程定义的流程实例,通过RuntimeServicestartProcessInstanceByKey方法
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");

注意: 它和startProcessInstanceById方法不同:这个方法期望使用Activiti引擎在发布时自动生成的id.可以通过调用processDefinition.getId() 方法获得这个值,生成的id的格式为 key:version, 最大长度限制为64个字符, 如果在启动时抛出了一个ActivitiException: 说明生成的id太长了,需要限制流程的key的长度

  • name: 这个属性是可选的, 对应ProcessDefinitionname属性.引擎自己不会使用这个属性,是用来在用户接口显示便于阅读的名称

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组
    • 当流程达到空结束事件就会结束
    • 这些元素都使用连线连接,这些连线拥有sourcetarget属性,定义了连线的方向
<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结构,对业务流程进行以下方面的:

    • 定义网关来实现决策环节: 经理可以驳回财报,重新给会计创建一个任务
    • 考虑使用变量: 可以保存或引用报告,把它显示到表单中
    • 在流程最后加入服务任务: 把报告发给每个领导

Activiti中工作流的生命周期详细解析!一个BPMN流程示例带你认识项目中流程的生命周期的更多相关文章

  1. 一个BPMN流程示例带你认识项目中流程的生命周期

    摘要:本文详细说明了在工作流Activiti框架中的BPMN流程定义整个运行的生命周期. 本文分享自华为云社区<本文详细说明了在工作流Activiti框架中的BPMN流程定义整个运行的生命周期& ...

  2. 【转】UML中的几种关系详细解析

    UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现 类与类图 1) 类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性.操作.关系的对象集合的总称. 2) 在系统中, ...

  3. Postman----登录接口返回的reponse中token值传递给其他接口的一个简单接口测试示例

    注: 在进行接口测试时,我们都需要使用登录,并且其他的接口都要在登录后进行,那么必不可少的会使用到将登录接口的reponse返回结果中的某些参数值需要进行返回,并传递给其他接口,这样才可以进行登录后的 ...

  4. java类生命周期详细解析

    (一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前 ...

  5. Android Activity生命周期详细解析

    概况 讲Android Activity那怎么都绕不过这张图,这篇文章也是围绕这幅图详细分析. 背景 假设这是你的APP,以此为背景,下面的每个part请结合上图理解. #Case 1 当按下app启 ...

  6. vue-cli中的build.js配置文件详细解析

    转载自:https://www.cnblogs.com/ye-hcj/p/7096341.html这是vue-cli脚手架工具的生产环境配置入口 package.json中的"build&q ...

  7. MDK中STM32使用Printf函数详细解析【转载】

    在用MDK调试STM32板子串口时,为了方便串口调试,调用了printf()函数,用Keil仿真是,串口不能正确的输出,软件仿真时,总是卡在那 里.有点纳闷,然后调用USART_SendData()函 ...

  8. shell中的find和xargs详细解析

  9. java:activiti(工作流简介 )

    1.工作流:(workflow) 整个工作的流程 eg:请假工作流 (我)员工-->组长-->经理-->主管-->人事-->总经理(董事会) eg:出差(报账)工作流 ( ...

随机推荐

  1. GO-02-helloworld

    package main /** package main表示一个可独立执行的程序,每个Go应用程序都需要包含一个名为main的包, 并且该包包含一个叫main()的函数(该函数是Go可执行程序的执行 ...

  2. 数据驱动 - 不同数据源的读取方式(ddt、数据文件、mysql)

    1. ddt 装饰器传参 2. ddt 读取数据文件 3. 读取 txt 文件 4. 读取 excel 文件 5. 连接 mysql 1. ddt 装饰器传参 python 的数据驱动模块 ddt 安 ...

  3. Ionic5整合极光推送JPush ( 简单 )

    项目初始化 1. 安装项目依赖: # 安装cordova插件 ionic cordova plugin add jpush-phonegap-plugin --variable APP_KEY=&qu ...

  4. [BUAA2021软工助教]案例分析作业总结

    目录 一.作业链接 二.优秀作业推荐 A+作业推荐 A作业推荐 三.总结 所有案例分析总结 特色与优点 问题与建议 不同类产品案例分析Bug汇总 CSDN问答社区.Stack Overflow.Seg ...

  5. go gin框架和springboot框架WEB接口性能对比

    1 简要概述 最近看起go lang,真的被go的goroutine(协程)惊艳到了,一句 go function(){#todo},即可完成一个并发的工作. 看到gin这个web框架时,突然就特别想 ...

  6. Git 常用命令总结,将会持续更新

    平常在windows电脑上使用Git Extensions 工具比较多,大部分的常用指令都可以通过点点点就可以完成.在mac电脑上的话使用sourcetree工具.但有时候也会直接通过git命令,很多 ...

  7. POJ3692 最大点权独立集元素个数

    题意:      n个男孩和m个女孩,给你他们谁和谁彼此了解,问你要找到一个集合,使得这个集合中的男孩和女孩相互了解,并且人数最多. 思路:      简单题目,其实就是在求最大点权独立集元素个数,先 ...

  8. hdu4888 最大流(构造矩阵)

    题意:       让你构造一个矩阵,满足每一行的和,和每一列的和都等于他给的,还要判断答案是否唯一,还有一点就是矩阵内所有的数字都是[0,k]范围的. 思路:       这个题目看完就让我想起了h ...

  9. Elasticsearch exception [type=mapper_parsing_exception, reason=No type specified for field [X]

    可能原因是实体类属性没有指定映射类型 创建mapping时需要指定field的type,如果不指定则报错 错误 //这是一个类中的字段 @Field(store = false) private St ...

  10. python-内置函数-compile,eval,exec

    #将字符串,编译成python代码 compile()#执行,有返回值,执行表达式并获取结果 eval()#执行python代码,无返回值,接收:代码或者字符串 exec() s = "pr ...