Activiti 介绍
简介
- Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准。 Activiti是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。 Activiti将提供宽松的Apache许 可2.0,同时促进Activiti BPM引擎和BPMN 2.0的匹配。
- 应用场景: 多人协作的(或者需要动态变动)的业务流程场景。
- 下载和安装: Activiti 官方网站: activiti.org/download.ht… Activiti 文档地址:www.activiti.org/userguide/#… Spring Boot:此处引用6.0版本
特别说明 工作流自动引spring-security入依赖,不需使用spring-security 时要排除掉,如果你的项目使用到Jsp的El 表达式,请同时排除引擎自带的el包
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</exclusion>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
<exclusion>
<groupId>de.odysseus.juel</groupId>
<artifactId>juel-spi</artifactId>
</exclusion>
</exclusions>
</dependency>
配置:此处默认是Spring Boot配置
spring:
activiti:
check-process-definitions: true
db-history-used: true
database-schema-update: true
history-level: full
process-definition-location-prefix: classpath:/processes/
配置说明
- check-process-definitions:自动验证部署设置。
- db-history-used :是否开启历史记录,一般建议开启。
- database-schema-update:是否每次都更新数据库。
- history-level: 历史记录级别。
-
none: 跳过所有历史归档。这是运行时流程执行的最高性能,但没有可用的历史信息。 -
activity:归档所有流程实例和活动实例。在流程实例结束时,顶级流程实例变量的最新值将被复制到历史变量实例中。不会存档任何详细信息。 -
audit: 这是默认的。它归档所有流程实例、活动实例,保持变量值持续同步以及提交的所有表单属性,以便通过表单进行的所有用户交互都是可追溯的并且可以被审计。 -
full:这是历史存档的最高级别,因此是最慢的。此级别存储级别中的所有信息audit以及所有其他可能的详细信息,主要是流程变量更新。
- process-definition-location-prefix: 本地流程文件前缀
因为项目使用了自定义数据源,所以排除掉Spring Boot自带的自动配置类
@SpringBootApplication(exclude = {org.activiti.spring.boot.SecurityAutoConfiguration.class,DataSourceAutoConfiguration.class})
数据库表说明
引擎使用jpa自动创建数据表,一共28张。
- ACT_RE_ :RE代表repository. 带有此前缀的表包含静态信息,例如流程定义和流程资源(图像、规则等)。
- ACT_RU_ :RU代表runtime. 这些是运行时表,包含流程实例、用户任务、变量、作业等的运行时数据。 Activiti 只存储流程实例执行期间的运行时数据,并在流程实例结束时删除记录。这使运行时表保持小而快。
- ACT_ID_ :ID代表identity. 这些表包含身份信息,例如用户、组等。
- ACT_HI_ :HI代表history. 这些是包含历史数据的表,例如过去的流程实例、变量、任务等。
- ACT_GE_ :general数据,用于各种用例。
Api
引擎Api围绕ProcessEngine这个接口来衍生。ProcessEngines.getDefaultProcessEngine()将在第一次调用时初始化 并构建流程引擎,然后总是返回相同的流程引擎。可以使用ProcessEngines.init() 和正确创建和关闭所有流程引擎 ProcessEngines.destroy()。当然你也可以直接在Spring容器里面把此引擎注入,注入后可直接调用其子接口的方法。
@Autowired private ProcessEngine processEngine;
processEngine.getTaskService();
processEngine.getRuntimeService()
如果你明确你需使用ProcessEngine接口下的哪个接口,比如RuntimeService接口,你可以直接注入。
@Autowired private RuntimeService runtimeService;
API各常用接口说明如下:
- Runtimeservice 运行信息操作接口,一般操作正在运行没结束的流程 TaskService 任务操作接口,节点的审核流转使用。
- IdentityService 身份认证接口,一般用不到,因为我们的项目都有角色权限。
- RepositoryService 静态信息操作接口,比如流程定义,模型等等。
- FormService 表单接口,动态获取流程的开始节点/节点的表单,此接口为可选,一般有动态表单的流程才会用到。 HistoryService 改接口操作暴露在Activiti的引擎收集的所有历史数据。执行流程时,引擎可以保留很多数据(这是可配置的)和历史记录级别有关。
- ManagementService 它允许检索有关数据库表和表元数据的信息。一般写自定义sql操作数据库时才会使用。
- DynamicBpmnService 可以用来改变流程定义的一部分,而无需重新部署。例如,您可以更改流程定义中用户任务的受理人定义,或更改服务任务的类名。
流程图和部署使用
流程图
我们看到的是一个无开始事件(左侧的圆圈),随后是两个用户任务:“编写月度财务报告”和 “验证月度财务 报告”,以无结束事件结束(右侧有粗边框的圆圈)
- 在(无)启动事件告诉我们什么入口点的过程。
- 在用户任务声明是我们的过程的人工任务的表示。请注意,第一个任务分配给会计组,而第二个任务分配给管理 组。有关如何将用户和组分配给用户任务的更多信息,请参阅用户任务分配部分。
- 当到达无结束事件时,该过程结束。
- 元素通过序列流相互连接。这些序列流有一个source和target,定义了序列流的方向。
那么流程图如何画呢?
- 使用activiti自带的画图工具(集成极其麻烦,感兴趣的朋友可以自行查找。)
- BPMN.js bpmn.js是一个BPMN2.0渲染工具包和web建模器,使用JavaScript编写,在不需要后端服务器支持的前提下向现 代浏览器内嵌入BPMN2.0流程图。这使得它很容易的嵌入到任何web应用中。 详细教程参考 www.jianshu.com/p/5b6567fe6…
流程图元素
-
任务节点
图中的圆圈和长方形就是一个节点,他们分别是开始节点和任务节点,均可拥有动态的流程表单(可选)。任 务节点上有Assignee属性,该属性为一般设置为审核人id或角色id,然后根据此id和角色或者用户表关联起来查询此 任务需要谁来审批。
-
网关。
图中的菱形是网关,网关一般有几种,分别是
- 排他网关:顾名思义就是排他,只能选择一条分支,若多条或没有符合的条件会抛出异常。使用排他网关时,必 定有且只有一条符合流程走向。
- 并行网关:可以使流程分开2个或多个分支并发执行。拥有分支和汇聚两个节点,当有一个并发网关做分支节点 时,必定有一个并行网关做汇聚节点与之对应。 需要注意的是并行网关不会解析其条件,就算给它条件他也不会解析,直接忽略,而且当某一个分支执行完到达 汇聚分支时,其他符合条件的分支没有执行完的话,已经执行完的分支会等待其他没执行完的分支,直到其他分 支执行完或手动结束。
- 复合网关:其聚集了并行网关和排他网关功能于一身,功能及其强大。 和排他网关相比,它多了分支和汇聚功能。 和并行网关相比它多了条件解析功能,即所有符合条件的分支都会被执行,然后在汇聚节点等待其他符合条件但没执行完的分支,直到他们执行完或者手动结束。
- 事件网关:基于事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流 程到达一个基于事件网关,网关会进入等待状态:会暂停执行。 与此同时,会为每个外出顺序流创建相对的事件订阅。
-
连线
使用箭头将节点和事件以及网关等元素连接起来,连线上可编写条件,也可设置默认,默认情况下的条件将被忽 略。条件为true的连线将被执行。条件表达式使用UEL表达式如:
<![CDATA[${order.price > 100 && order.price < 250}]]>
${order.price > 100 && order.price < 250}
部署
特别说明: 此处仅说明从数据库部署的情况。通过RepositoryService接口查询数据库获取到模型的id等信息,然后通过编程的方式就行部署,可以动态的部署流程,而不必更换本地文件。
Deployment deployment = repositoryService.createDeployment()
.addString("模型名" + ".bpmn20.xml", "模型的xml字符串")
.name("模型名").key("your Key")
.deploy();
部署成功后可以在 act_re_procdef 和 act_re_deployment 表看到其相关信息,并且其 act_re_procdef 表的 DEPLOYMENT_ID_字段会对应上act_re_deployment的主键id。
发起流程
部署好流程图后即可发起流程。 使用Runtimeservice 接口是statrxxx方法根据其参数可发起流程形成一个流程实例。这里简单说明一下,模型、流程定义、流程实例的关系,比较抽象。
模型:保存了流程定义的规则,可理解为Java的class文件。流程定义:保存了模型和部署之间的关系和版本。可理解为Java里面的属性字段。
流程实例:每一个流程定义发起都有一个实例对应。可理解为Java的new出来的对象。
如图所示,启动流程有很多种方法。我一般使用ById的方法。
此处可看到,其重载了多个方法,进去源码可以看到其各个参数的作用。取最多参数的方法来一一说明。
public ProcessInstance startProcessInstanceById(String processDefinitionId,String businessKey,Map<String, Object> variables) {
return (ProcessInstance)this.commandExecutor.execute(
new StartProcessInstanceCmd((String)null, processDefinitionId, businessKey, variables));
}
- processDefinitionId:流程定义id
- businessKey:业务键,自定义 variables:流程变量,Map<String, Object> ,流程全局均可访问。流程发起后,会在act_hi_procinst表创建一条数据余其实例对应,若其拥有一个需要审核的节点的话,在act_ru_task 表中有一条或多条数据的 PROC_INST_ID_ 与 act_hi_procinst 表的id对应。
查询
Activiti提供的查询Api及其丰富,基本上满足我们日常所需的查询。一般使用了最高的是以下这四个。
-
historyService
历史查询Api,在历史记录级别为FULL的情况下可查询流程历史的每一个细节。其接口部分源码如下:
public interface HistoryService {
//创建历史实例查询器
HistoricProcessInstanceQuery createHistoricProcessInstanceQuery();
//创建历史活动查询器
HistoricActivityInstanceQuery createHistoricActivityInstanceQuery();
//创建历史任务查询器
HistoricTaskInstanceQuery createHistoricTaskInstanceQuery();
//创建历史详情查询器
HistoricDetailQuery createHistoricDetailQuery();
}
创建查询器后,便可使用Api链式调用,其提供的Api见名知意通俗易懂,还提供条件筛选和排序等等功能。就比如HistoricProcessInstanceQuery
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
//创建查询器后可根据其实现类的属相,进行条件筛选,其实现类是org.activiti.engine.impl下的
HistoricProcessInstanceQueryImpl.processInstanceId("ProcessId")//实例id为xxx的实例
.orderByProcessInstanceStartTime()//进行排序
.singleResult();//取单条实例。
查询完毕后通过historicProcessInstance对象便可获得其实例的各种信息,比如实例id、实例创建时间、实例结束时间等等具体属相请查看org.activiti.engine.impl.persistence.entity包下的HistoricProcessInstanceEntityImpl。
-
runtimeService
运行查询Api,用于查询流程运行时的各种各样的数据,其位于org.activiti.engine包下的RuntimeService接口。其接口下方法非常多,抽取部分说明。部分源码如下:
public interface RuntimeService {
//根据流程定义的Key发起流程
ProcessInstance startProcessInstanceByKey(String var1, String var2);
//根据流程定义id发起流程
ProcessInstance startProcessInstanceById(String var1, String var2, Map<String, Object> var3);
//根据执行id获取流程变量
Map<String, Object> getVariables(String var1);
//创建实例查询器,和historyService不同的是,他只能查询正在运行的实例。
ProcessInstanceQuery createProcessInstanceQuery(); }
-
taskService
任务节点查询器,用于查询正在运行的节点的各种信息,比如审核人、当前节点名称、创建时间等等。 taskService位于org.activiti.engine包下,部分源码如下:
public interface TaskService {
//创建节点查询器获取节点相关信息,如节点id,节点的实例id等等
TaskQuery createTaskQuery();
//完成节点推进下一级节点(若有) void complete(String var1);
//设置审核人,绘制流程图时拖没指定审核人可用此方法设定
void setAssignee(String var1, String var2);
//设置流程变量,全局可见
void setVariables(String var1, Map<String, ? extends Object> var2);
//设置局部变量,当前节点可见
void setVariablesLocal(String var1, Map<String, ? extends Object> var2);
//添加批注,参数分别是节点Id,实例Id,批注信息
Comment addComment(String var1, String var2, String var3);
//获取批注
Comment getComment(String var1);
//根据节点id获取附件
List<Comment> getTaskComments(String var1);
//获取附件
Attachment getAttachment(String var1);
//添加附件 参数分别是 var1附件类型,var2节点id,var3实例id,var4附件名称,var5附件描述,var6附件 url
Attachment createAttachment(String var1, String var2, String var3, String var4, String var5,
String var6);
}
-
repositoryService
仓库查询器一般用于查询模型、流程定义和已部署的流程。repositoryService位于org.activiti.engine下,部分源码如下所示:
public interface RepositoryService {
//创建流程定义查询器
ProcessDefinitionQuery createProcessDefinitionQuery();
//创建部署查询器
DeploymentQuery createDeploymentQuery();
//创建模型查询器
ModelQuery createModelQuery();
//获取模型
Model getModel(String var1);
}
与业务关联
以上都是流程引擎单独运行的情况,我们日常开发在免不了与自己的业务相结合,那么怎么与业务结合起来呢?在act_re_deployment 和act_re_model 以及表里有个Key字段,这个字段可以标记不同的流程属于那个部门或公司用于区分流程的所属者。
在act_hi_procinst 表里面有一个Activiti特意留给我们的和业务关联的 BUSSINESS_KEY_ 在创建实例时可以指定,并且用此bussinessKey关联到业务表里面去。举个例子,现在有一张用户表 user_leave
CREATE TABLE `user_leave` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
`days` bigint(2) NOT NULL COMMENT '天数',
`reason` varchar(255) NOT NULL,
`type_id` int(10) NOT NULL COMMENT '请假类型id',
`leave_key` varchar(30) NOT NULL COMMENT '关联工作流的key',
`user_id` int(10) DEFAULT NULL COMMENT '请假人的id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
此表为用户请假信息表,leave_key 为流程关联键与流程实例的BUSSINESS_KEY_ 相对应。leave_key 可用时间戳 或UUID保证其尽可能唯一,并且在开始流程时在方法里面传参,伪代码如下:
public StatusResult applyTask(@PathVariable("processDefId") String processDefId){
String leaveBusinessKey = DateTime.now()+"";
String uuid = UUID.randomUUID().toString();
//全局流程变量
HashMap map = new HashMap(); map.put("key1","value1"); map.put("key2","value2"); runtimeService.startProcessInstanceById(processDefId, leaveBusinessKey, map);
//或使用以下方法
runtimeService.startProcessInstanceById(processDefId, uuid, map);
}
创建完成后,user_leave 表的leave_key 会与act_hi_procinst 的 BUSSINESS_KEY_ 相对应,可用查询其中一张表就可以查询另外的流程信息或者用户请假信息。