实际生活中有一些例如办公类流程需要多个环节人员进行审批,以达到对整个工作流程的控制与掌握。为提高效率,借助计算机对业务流程自动化执行管理,而Flowable框架就可以帮助我们进行工作流程进行管理工作。而Flowable可以帮助我们即是业务流程发送了变更,我们的业务流程代码也无需进行更新。
如何做到我们在业务流程发生变更后,我们的业务系统代码可以不发生改变?原理如下:
将业务流程的每个节点读取到数据库中,这样每个节点(包括开始节点和结束节点)就是数据库中的一条记录,当发生业务流程的时候,不断的从业务流程图中读取下一个节点,其实就相当于操作节点对应的数据库记录,这样就实现流程管理和状态字段无关。
BPMN(Business Process Model And Notation),业务流程模型和符号,是由BPMI(Business Process Management Initiative)开发的一套的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。2004年5月发布了BPMN1.0规范。BPMI于2005年9月并入OMG(The Object Management Group,对象管理组织)组织。OMG于2011年1月发布BPMN2.0的最终版本。
下表是Activiti7 与flowable的相关功能特性对比说明。
对比项\引擎 | Activiti-7.x | Flowable-6.x |
---|---|---|
商业化 | √ | √ |
路线(Roadmap) | 云 | 工具型 |
PVM引擎 | × | × |
BPMN2引擎 | √ | √ |
CMMN引擎 | × | √ |
DMN引擎 | × | √(开源版支持不太好) |
[建模工具选型 | √(AngularJS) | √(AngularJS) |
建模工具内容 | BPMN2 | BPMN2/CMMN/DMN |
扩展节点(Mule\Http等) | × | √ |
Spring Boot | √ | √ |
Spring Cloud | √ | × |
Web控制台 | √ | √ |
Rest接口 | √ | √ |
历史异步归档 | × | √ |
异步任务全局锁 | × | √ |
Activiti在目前来看有点不思进取,核心功能和内核的优化并没有太大进步,着力点全在商业版和云上面,核心只支持BPMN2协议,跟6版本没有什么区别。如果你是一个老的Activiti使用者,并且只是用BPMN2协议,可以选用Activiti(非Cloud版本)。
Flowable不管是功能层面还是在代码层面来讲,都是这2个中最重的,当初跟Activiti分道扬镳的原因也是因为理念不一样,Flowable更注重其功能性、和性能。在上面表格中,历史异步归档和全局锁都是对性能的极大优化,特别是异步任务这一项,当年在使用Activiti的使用是一个极大的困扰,因为异步任务的吞吐反而会随着实例数的增加而加速恶化。Flowable比较臃肿,它支持了太多的东西,以致于如果想做POC或者Demo,环境搭建这一步都够呛。但是如果你本身就想做一个扩展性强的,性能高的工作流平台(SaaS\PaaS),Flowable是不二的选择。
而且flowable和activiti6是同一个团队开发的,activiti先开发,flowable后开发,所以flowable 算是 activiti6的升级版。
首先导入Flowable相关Pom依赖如下所示:
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<logback.version>1.2.3logback.version>
properties>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-engineartifactId>
<version>6.3.0version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-accessartifactId>
<version>${logback.version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>${logback.version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>${logback.version}version>
dependency>
dependencies>
新增测试类初始化Flowable数据库表的配置类如下:
private ProcessEngineConfiguration processEngineConfiguration;
public DataSource initDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://1.14.49.145:23306/flowable6?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true");
dataSource.setUsername("root");
dataSource.setPassword("Xxxxx");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setMaxActive(3);
dataSource.setMinIdle(1);
return dataSource;
}
/**
* 初始化数据库表34张表
*/
@Before
public void initDataBaseTables() {
processEngineConfiguration = new StandaloneProcessEngineConfiguration();
processEngineConfiguration.setDataSource(initDataSource());
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
processEngineConfiguration.buildProcessEngine();
}
运行测试类输出如下结果:同时查看Flowable数据库,发现新增了34张表如下所示:
Flowable的表都是以ACT_开头。第二部分是表示表的用途的两个字母标识。用途也和服务的API对应。
ACT_RE_:'RE’表示Repository。这个前缀的表包含了流程定义和流程静态资源(图片、规则等等)。
ACT_RU_:'RU’表示Runtime。这些运行时的表,包含流程实例,任务、变量,异步任务等运行中的数据。Flowable只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这些运行时表可以一直很小并且速度很快。
ACT_HI_:'HI’表示History。这些表包含历史数据,比如历史流程实例,变量,任务等等。
ACT_GE_:'GE’表示General。通用数据,用于不同场景下。
ACT_ID_*:'ID’表示Identity。组织结构,包含标识的信息,如用户,用户组,等等。
flowable 默认重要服务体系图如下所示:
Flowable的资源管理类,提供了管理和控制流程发布包和流程定义的操作。除了部署定义流程之外,此服务还支持查询引擎的发布包和流程定义。
Flowable的流程运行管理类,可以从这个服务类中获取很多关于流程执行的相关信息。
Flowable的任务管理类,可以获取当前任务信息。
Flowable历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据,比如流程实例启动时间,任务参与者等等。
Flowable引擎管理类,提供了Flowable流程引擎管理的管理和维护功能,主要用于Activity系统的日常维护。
下面我们将使用Flowable提供的相关service实现一个简单流程入门。
Flowable官网给我们提供了一个请假流程图如下所示:
首先这个流程开启了一个请假流程,此请求会被通过或拒绝,若请求通过,就进入外部系统去通过假期请求并结束整个流程。若请求拒绝就发送拒绝邮件然后结束整个流程。
Flowable支持BPMN2,0,所以整个流程可以用xml进行表示,所以上面的流程图xml定义如下所示:
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
<userTask id="approveTask" name="Approve or reject request"/>
<sequenceFlow sourceRef="approveTask" targetRef="decision"/>
<exclusiveGateway id="decision"/>
<sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<serviceTask id="externalSystemCall" name="Enter holidays in external system"
flowable:class="org.flowable.CallExternalSystemDelegate"/>
<sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
<userTask id="holidayApprovedTask" name="Holiday approved"/>
<sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
<serviceTask id="sendRejectionMail" name="Send out rejection email"
flowable:class="org.flowable.SendRejectionMail"/>
<sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
<endEvent id="approveEnd"/>
<endEvent id="rejectEnd"/>
process>
definitions>
现在我们可借助于RepositoryService 完成流程部署,首先将上面流程图片与流程XML保存在resource目录下如下所示:
然后将此流程发布到Flowable引擎代码如下所示:
/**
* 部署请假流程
*/
@Test
public void deployWorkflow() {
// 部署流程
RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
// 关联resource下xml文件与图片
Deployment deployment = repositoryService.createDeployment().addClasspathResource("diagram/holiday-request.png")
.addClasspathResource("diagram/holiday-request.bpmn20.xml")
// 命名并部署
.name("请假流程").category("人事流程管理").key("holiday-request").deploy();
System.out.println("部署id:" + deployment.getId());
System.out.println("部署名称:" + deployment.getName());
}
运行结果如下所示:
可以在表 act_re_deployment 中 查看到此次流程部署信息入下图所示:
xml流程定义的内容被解析并将相关信息保存在 act_re_procdef 中如下所示:
Xml文档与流程图片直接保存在 act_ge_bytearray 中如下所示:
Flowable提供了相关API可以让我们查询流程部署的相关信息,我们同样可借助RepositoryService构建_ProcessDefinitionQuery_ 对象,我们可以通过act_re_deployment 部署id为1的记录关联查询流程定义信息,act_re_deployment 表记录如下所示:
流程定义表内容如下所示:
查询代码如下所示:
/**
* 查询部署信息与请假流程信息
*/
@Test
public void queryDeployWorkflow() {
// 部署流程
RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId("1")
.singleResult();
System.out.println("Found process definition : " + processDefinition.getName());
System.out.println("Found process definition : " + processDefinition.getKey());
System.out.println("Found process definition : " + processDefinition.getCategory());
System.out.println("--------------------------------------------------------------");
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId("1").singleResult();
System.out.println("Found deployment : " + deployment.getName());
System.out.println("Found deployment : " + deployment.getCategory());
System.out.println("Found deployment : " + deployment.getKey());
}
运行结果如下:
当定义的流程并发布到Flowable引擎上时,我们使用RepositoryService提供 deleteDeployment 删除流程定义的相关内容信息,具体代码如下:
/**
* 删除流程
*/
@Test
public void removeWorkflow() {
RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
// 第一个指定删除的流程id信息,第二参数若为true,不管流程是否已经启动都会进行强制删除,一般不推荐使用
repositoryService.deleteDeployment("1",false);
}
我们检查相关的三个表act_ge_bytearray、act_re_deployment、act_re_procdef 定义的内容都为空了。需要注意的是 deleteDeployment 方法如果为true,不管流程是否已经启动都会进行强制删除,一般不推荐使用。
在上面的代码中我们已经将流程定义并发布到Flowable引擎上了,下面就可以启动定义的流程了,所以我们修改一下流程定义的xml并将流程重新部署发布到Flowable引擎上。
具体修改xml内容如下所示:
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
<userTask id="approveTask" name="Approve or reject request" flowable:assignee="zhangsan"/>
<sequenceFlow sourceRef="approveTask" targetRef="decision"/>
<exclusiveGateway id="decision"/>
<sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<serviceTask id="externalSystemCall" name="Enter holidays in external system"
flowable:class="org.flowable.CallExternalSystemDelegate"/>
<sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="lisi"/>
<sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
<serviceTask id="sendRejectionMail" name="Send out rejection email"
flowable:class="com.galengao.SendRejectMail"/>
<sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
<endEvent id="approveEnd"/>
<endEvent id="rejectEnd"/>
process>
definitions>
再次运行发布流程的代码,运行结果如下所示:
下面我们使用Flowable提供的RuntimeService提供的startProcessInstanceByKey方法开启一个流程实例,此方法可接收流程变量,具体代码如下所示:
/**
* 开启一个流程实例
*/
@Test
public void startProcessInstance() {
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", "zhangsan");
variables.put("duration", 5);
variables.put("description", "go climbing");
ProcessInstance processInstance =
// 开启流程实例
runtimeService.startProcessInstanceByKey("holidayRequest", variables);
String deploymentId = processInstance.getDeploymentId();
String activityId = processInstance.getActivityId();
System.out.println("Deployment Id: "+ deploymentId+" Activity Id: "+ activityId);
}
运行结果如下:
此次开启的流程实例可在act_ru_task 表查看如下所示;
而刚刚我们传入的Map参数保存在了act_ru_variable表中如下所示:
此次流程节点从开始流转到网关节点,一共经历了2个流程节点,我们可通过act_ru_execution 查看相关信息如下:
流程任务信息可通过TaskService的createTaskQuery方法进行查询,具体实现代码如下所示:
/**
* 查询流程任务
*/
@Test
public void queryProcessInstance(){
TaskService taskService = processEngineConfiguration.getTaskService();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("holidayRequest")
.taskAssignee("zhangsan").list();
for (Task task : list) {
String processInstanceId = task.getProcessInstanceId();
String name = task.getName();
String assignee = task.getAssignee();
String description = task.getDescription();
String id = task.getId();
System.out.println("processInstanceId: "+processInstanceId+" name: "+name+" assignee: "+assignee+" description: "+description+" id: "+id);
}
}
输出结果如下所示:
在流程xml定义文件中我们可以查看到如下内容:
Flowable提供了JavaDelegate接口,通过此接口可以做额外业务逻辑处理,例如当审批拒绝后实现发送拒绝邮件的逻辑,我们的发送邮件代码可以这样实现:
public class SendRejectMail implements JavaDelegate {
/**
* Flowable的触发器
*/
@Override
public void execute(DelegateExecution delegateExecution) {
// TODO 此处省略真实发送邮件逻辑
System.out.println("发送拒绝邮件通知------");
}
}
然后再来编写拒绝流程的相关代码如下所示:
/**
* 拒绝流程,发送拒绝邮件
*/
@Test
public void completeProcessInstanceTask() {
TaskService taskService = processEngineConfiguration.getTaskService();
Task task = taskService.createTaskQuery().processDefinitionKey("holidayRequest")
.taskAssignee("zhangsan").singleResult();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("approved",false);
taskService.complete(task.getId(),variables);
}
运行结果如下所示:
同意流程与拒绝流程类似,只需要将上面变量Map **variables 的 **approved 设置为true即可。
JavaDelegate 接口是个非常有用的接口,我们可以通过此接口在审批通过或者拒绝时,额外进行相关业务处理。
选择使用Flowable这样的流程引擎的众多原因之一是,它自动存储所有流程实例的审计数据或历史数据,这对我们进行后续进行工作汇总、报告提供前期的相关数据支持,例如我们想看到流程运行的时长,我们可借助HistoryService实现我们的需求,具体代码如下所示:
/**
* 查询流程历史信息
*/
@Test
public void queryHistoryProcessInstance(){
HistoryService historyService = processEngineConfiguration.getHistoryService();
// 查询流程历史激活信息
HistoricActivityInstanceQuery activityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
// 查询结束的流程信息
List<HistoricActivityInstance> activityInstances = activityInstanceQuery.processDefinitionId("holidayRequest:1:4").finished()
// 按照流程结束时间倒排查询
.orderByHistoricActivityInstanceEndTime().desc().list();
for (HistoricActivityInstance instance : activityInstances) {
String activityName = instance.getActivityName();
String activityId = instance.getActivityId();
Long durationInMillis = instance.getDurationInMillis();
System.out.println("Activity: " + activityName + " activityId:" + activityId + " durationInMillis:" + durationInMillis);
}
}
本文Flowable和BPMN 2.0的概念和术语,同时借助与Flowable 提供的各种Service完成如下内容:
后面我们将继续更新Flowable相关内容,敬请期待。
https://gitee.com/codegeekgao/frameworks-learn/tree/master/flowable-element