一、核心思想
理解:
Activiti核心思想,实际上就是提供一系列的API,对他规定的23张数据库表进行操作。所有的数据流转,都是在这23张表里面完成,其中RE表存储流程
部署相关信息,RU表存储运行时信息,HI表存储历史信息。通过表里面的业务键,与具体的业务信息关联起来。
通过流程设计插件,画BPMN流程图,实际上就是xml文件,规定了一个流程里面的每个节点的信息,每个节点可以通过设置变量的方式,填充一些信息,
比如审批人/审批组等信息。在运行的时候,通过API里面提供的参数和方法,将具体的值填充到这些变量。
一个流程部署信息,就相当于模板,可以根据这个模板启动多个流程实例。
二、核心API
(1)、ProcessEngineConfiguration和ProcessEngine
ProcessEngineConfiguration实际上就是一个配置类,用于加载配置文件里面的信息,配置文件
activiti.cfg.xml里面配置了数据源信息、生成表的策略、事务管控等。
通过这个流程配置对象,可以生成ProcessEngine流程引擎,其它的所有对象都是基于流程引擎生成的,可以说这是一个门面对象。
灵活的读取配置对象的方式:
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
如果将 activiti.cfg.xml ⽂件名及路径固定,且 activiti.cfg.xml ⽂件中有 processEngineConfiguration 的配置,也可以使用简单方式:
使⽤classpath下的activiti.cfg.xml中的配置创建processEngine ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();
(2)、RepositoryService
资源服务类,用于管理和部署流程,发布流程定义,查看流程定义信息
(包括发布包里面的流程图和文件),暂停和激活流程,总而言之就是管理流程定义部署的资源。
(3)、RuntimeService
流程运行管理类,管理和查看流程实例,启动流程、暂停/激活流程、设置流程变量等待,
总而言之就是管理流程实例。
(4)、 TaskService
任务管理类,管理和查看任务,执行任务\拾起任务\归还任务、设置任务执行人,总而言之,就是
对一个流程实例里面的任务节点进行管理和查询。
(5)、HistoryService
历史信息管理类,管理查询历史操作信息,流程实例在每个节点的操作,都会记录在历史表里面,
⽐如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执⾏路径等等。
三、基本流程
(1)、流程定义:
Activiti-Designer流程图
流程元素:
Connection—连接
Event—事件
Task—任务
Gateway—⽹关
Container—容器
Boundary event—边界事件
Intermediate event- -中间事件
流程定义完成之后,保存bpmn文件。
1、在流程图的properties上指定流程定义的 key,后面启动流程实例要通过这个key来启动,
才能生成对应的实例。
2、在每个任务节点上指定负责人(或者候选人/候选人组),可以通过写死的方式或者uel表达式的方式
3、对于有分支的情况,可以在连接上指定判断条件
4、有分支的情况,可以通过网关来解决,网关分为排他网关(一个流程实例只能通过条件选择一个方向,串行执行),和并行网关(可以分发到不同的节点)。
(2)、流程部署
将上面绘制的流程定义图(实际上就是一个xml),部署到Activiti上(实际上就是把这些
数据插入到RE资源表),这些操作通过Activiti提供的api来完成
// 获取
repositoryService RepositoryService repositoryService = processEngine.getRepositoryService();
//部署对象
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("diagram/myholiday.bpmn")// bpmn⽂件
.addClasspathResource("diagram/myholiday.png")// 图 ⽚ ⽂件
.name("请假申请流程").deploy();
(3)、流程实例启动
根据流程部署的key,启动一个实例,一个流程就按照流程定义图开始了,实际上就是往ru和hi表插入了一条实例数据。
//定义流程变量
Map<String, Object> variables = new HashMap<String, Object>();
//设置流程变量assignee
variables.put("assignee", "张三");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
(4)、任务查询
流程流转到不同的节点,对应节点上的负责人查询⾃⼰当前需要处理的任务,查询出来的任务都是该⽤户的待办任务。
// 任务负责⼈
String assignee = "zhangsan";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("myholiday01")
.taskAssignee(assignee).list();
(5)、任务处理
任务负责⼈查询待办任务,选择任务进⾏处理,完成任务。实际上这里就是更新re表和插入数据到hi表。
//任务id
String taskId = "8305";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
//完成任务
taskService.complete(taskId);
(6)、其它细节
<1>、流程定义部署的其它方式:
通过zip的方式进行部署
// 定义zip输⼊流
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(" diagram/holiday.zip"); ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
<2>、查询器
一般查询都是通过查询器对象进行查询,例如流程定义信息的查询
// 流程定义key
String processDefinitionKey = "holiday";
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 查询流程定义
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//遍历查询结果
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
<3>、流程定义删除
删除已经部署成功的流程定义。一般用级联删除,删除流程定义的同时,也会把流程实例也删除掉。
// 流程部署id
String deploymentId = "8801";
// 通过流程引擎获取
repositoryService RepositoryService repositoryService = processEngine.getRepositoryService();
//删除流程定义, 如果该流程定义已有流程实例启动则删除时出错 repositoryService.deleteDeployment(deploymentId);
repositoryService.deleteDeployment(deploymentId, true);
<4>、业务标识
启动流程的时候,可以将业务key存入到Activiti的表中,这样就可以关联上业务信息,
或者也可以将流程实例id存入到业务表
// 根据流程定义的key和业务key启动⼀个流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey);
//获取业务key
String businessKey = processInstance.getBusinessKey();
<5>、挂起、激活流程实例
挂起流程定义,则属于该流程定义的流程实例全部暂停,并且不能启动新的流程实例。
// 获得流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId).singleResult();
//是否暂停 boolean suspend = processDefinition.isSuspended();
if(suspend){
//如果暂停则激活,这⾥将流程定义下的所有流程实例全部激活 repositoryService.activateProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定义: "+processDefinitionId+"激活"); }
else{
//如果激活则挂起,这⾥将流程定义下的所有流程实例全部挂起 repositoryService.suspendProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定义: "+processDefinitionId+"挂起");
}
也可以单独暂停和激活一个流程实例,不会影响其它实例
//根据流程实例id查询流程实例
ProcessInstance processInstance =
runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
boolean suspend = processInstance.isSuspended();
if(suspend){
//如果暂停则激活
runtimeService.activateProcessInstanceById(processInstanceId); System.out.println("流程实例: "+processInstanceId+"激活");
}else{
//如果激活则挂起
runtimeService.suspendProcessInstanceById(processInstanceId); System.out.println("流程实例: "+processInstanceId+"挂起");
}
<6>、监听器
针对任务,可以设置监听器,在不同的时候触发,必须实现 org.activiti.engine.delegate.TaskListener 接⼝
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发⽣都触发
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//这⾥指定任务负责⼈ delegateTask.setAssignee("张三");
}
}
<7>、变量流程
流程变量作用域分为全局(流程实例内都有效)和局部(任务节点或者执行实例内有效),
类型可以是pojo对象(须实现序列化接⼝ serializable)
流程变量可以在启动时设置/任务办理时设置/通过当前流程实例设置
// 启动流程时设置流程变量
@Test
public void startProcessInstance() {
Holiday holiday = new Holiday();
holiday.setNum(3);
// 定义流程变量
Map<String, Object> variables = new HashMap<String, Object>();
//变量名是num,变量值是holiday.getNum(),变量名也可以是⼀个对象
variables.put("num", holiday.getNum());
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
//任务办理时设置
taskService.complete(taskId, variables);
//通过流程实例id设置
runtimeService.setVariable(executionId, "holiday", holiday);
//通过任务设置流程变量
taskService.setVariable(taskId, "holiday", holiday);
//⼀次设置多个值
taskService.setVariables(taskId, variables)
// 设置local变量,作⽤域为该任务
taskService.setVariablesLocal(tasked, variables);
<8>、Candidate-users 候选⼈(组任务)
在流程图中任务节点的配置中设置 candidate-users(候选⼈)
根据候选⼈查询组任务,然后拾起任务,其它的候选人就不能再拾起,当前用户变成负责人。
并且还可以转发给其它人和退回(本质上就是设置负责人这个流程变量,
设置为null就是退回组
//拾取任务 即使该⽤户不是候选⼈也能拾取(建议拾取时校验是否有资格)
//校验该⽤户有没有拾取任务的资格
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskCandidateUser(userId)
.singleResult();
if(task!=null){
taskService.claim(taskId, userId);
}
// 如果设置为null,归还组任务,该 任务没有负责⼈
taskService.setAssignee(taskId, null);
//交接给其它人
taskService.setAssignee(taskId, candidateuser);
(7)、网关
<1>、 排他⽹关
经过网关,判断分支连线上设置的条件为true,就会走对应的分支,属于串行,
经过排他⽹关必须要有⼀条且只有⼀条分⽀⾛。
如果都不满足,就会抛出异常,这也是用排它网关和用连线直接设置条件的区别,不会造成异常流程结束而没有感应的情况发生。
<2>、 并⾏⽹关
有两个作用,一个是fork分支,一个是join汇聚分支,
经过并行网关出去后,所有的分支都会创建一个并发流程,
输入到并行网关后,所有分支都会汇聚在一起,在并行网关可以设置达到什么条件(到达并行网关的分支比例/数量等),再进入到下个流程。
基于并行网关的这种特性,可以用来做会签功能。
与其他⽹关的主要区别是,并⾏⽹关不会解析条件。 即使顺序流中定义了条件,也会被忽略
<3>、包含⽹关
可以看做是排他⽹关和并⾏⽹关的结合体,又可以设置条件(只有条件为true的分支才会创建),
又可以分发流程和汇聚流程分支(只等待条件为true的分支到达)。