Activit(1基础部分)
1.介绍
Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发
维护成本。
Activiti是一个工作流引擎(其实就是一堆jar包API),业务系统访问(操作)activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起。
2.BPMN
BPMN(Business Process Model AndNotation)- 业务流程模型和符号 是由BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。
Bpmn图形其实是通过xml表示业务流程,上边的.bpmn文件使用文本编辑器打开:
<process id="holiday" isClosed="false" isExecutable="true" name="请假审批" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:assignee="${assignee0}" activiti:exclusive="true" id="_3" name="请假申请"/>
<userTask activiti:assignee="${assignee3}" activiti:exclusive="true" id="_4" name="人事存档"/>
<endEvent id="_6" name="EndEvent">
<extensionElements>
<activiti:executionListener class="group.ytx.core.tools.activiti.MyTaskListener" event="end"/>
</extensionElements>
</endEvent>
<userTask activiti:assignee="${assignee1}" activiti:exclusive="true" id="_7" name="主管审批"/>
<userTask activiti:assignee="${assignee2}" activiti:exclusive="true" id="_8" name="项目经理审批"/>
<sequenceFlow id="_9" sourceRef="_2" targetRef="_3"/>
<exclusiveGateway gatewayDirection="Unspecified" id="_15" name="ExclusiveGateway"/>
<sequenceFlow id="_16" sourceRef="_3" targetRef="_15"/>
<sequenceFlow id="_17" sourceRef="_15" targetRef="_7">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day>7}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_18" sourceRef="_15" targetRef="_8">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day<=7}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_19" sourceRef="_7" targetRef="_4"/>
<sequenceFlow id="_20" sourceRef="_8" targetRef="_4"/>
<sequenceFlow id="_21" sourceRef="_4" targetRef="_6"/>
</process>
3.Servcie服务接口
3.1ProcessEngine引擎
(1)调用Activiti的工具类来生成activiti需要的表结构
// 使用classpath下的activiti.cfg.xml中的配置来创建 ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 自定义的方式来加载配置文件
// 首先创建ProcessEngineConfiguration对象
ProcessEngineConfiguration configuration =
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 通过ProcessEngineConfiguration对象来创建 ProcessEngine对象
ProcessEngine processEngine = configuration.buildProcessEngine();
(2)Activiti 的表都以 ACT_ 开头
ACT_RE :'RE'表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快.
ACT_HI:'HI'表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE : GE 表示 general。 通用数据, 用于不同场景下
表分类 | 表名 | 解释 |
---|---|---|
一般数据 | ||
[ACT_GE_BYTEARRAY] | 通用的流程定义和流程资源 | |
[ACT_GE_PROPERTY] | 系统相关属性 | |
流程历史记录 | [ACT_HI_ACTINST] | 历史的流程实例 |
[ACT_HI_ATTACHMENT] | 历史的流程附件 | |
[ACT_HI_COMMENT] | 历史的说明性信息 | |
[ACT_HI_DETAIL] | 历史的流程运行中的细节信息 | |
[ACT_HI_IDENTITYLINK] | 历史的流程运行过程中用户关系 | |
[ACT_HI_PROCINST] | 历史的流程实例 | |
[ACT_HI_TASKINST] | 历史的任务实例 | |
[ACT_HI_VARINST] | 历史的流程运行中的变量信息 | |
流程定义表 | ||
[ACT_RE_DEPLOYMENT] | 部署单元信息 | |
[ACT_RE_MODEL] | 模型信息 | |
[ACT_RE_PROCDEF] | 已部署的流程定义 | |
运行实例表 | ||
[ACT_RU_EVENT_SUBSCR] | 运行时事件 | |
[ACT_RU_EXECUTION] | 运行时流程执行实例 | |
[ACT_RU_IDENTITYLINK] | 运行时用户关系信息 | |
[ACT_RU_JOB] | 运行时作业 | |
[ACT_RU_TASK] | 运行时任务 | |
[ACT_RU_VARIABLE] | 运行时变量表 |
3.2service服务
(3)RepositoryService
是activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此service将流程定义文件的内容部署到计算机。除了部署流程定义以外还可以:查询引擎中的发布包和流程定义。暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。获得流程定义的pojo版本, 可以用来通过java解析流程,而不必通过xml .
//部署流程定义
// 获取RepositoryService进行部署操作
RepositoryService service = engine.getRepositoryService();
// 使用RepositoryService进行部署操作
Deployment deploy = service.createDeployment()
.addClasspathResource("bpmn/evection.bpmn") // 添加bpmn资源
.addClasspathResource("bpmn/evection.png") // 添加png资源
.name("出差申请流程")
.deploy();// 部署流程
//查询流程的定义
// 获取一个 ProcessDefinitionQuery对象 用来查询操作
ProcessDefinitionQuery processDefinitionQuery =
repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> list =
processDefinitionQuery.processDefinitionKey("evection")
.orderByProcessDefinitionVersion() // 安装版本排序
.desc() // 倒序
.list();
// 删除流程定义,如果该流程定义已经有了流程实例启动则删除时报错
repositoryService.deleteDeployment("12501");
// 设置为TRUE 级联删除流程定义,及时流程有实例启动,也可以删除,设置为false 非级联删除操作。
//repositoryService.deleteDeployment("12501",true);
//流程定义挂起实例与激活
// 查询流程定义的对象
ProcessDefinition processDefinition =
repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("evection").singleResult();
// 获取当前流程定义的状态
boolean suspended = processDefinition.isSuspended();
repositoryService.activateProcessDefinitionById(
// 流程定义的id,是否激活,激活时间
id,true,null);
repositoryService.suspendProcessDefinitionById(
// 流程id,是否挂起,挂起时间
id,true,null);
RuntimeService
Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息
//启动流程实例
// 获取RuntimeService对象
RuntimeService runtimeService = engine.getRuntimeService();
// 根据流程定义的id启动流程
String id= "evection";
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey(id);
//流程实例挂起实例与激活
// 获取流程实例对象
ProcessInstance processInstance =
runtimeService.createProcessInstanceQuery()
.processInstanceId("25001").singleResult();
// 获取相关的状态操作
boolean suspended = processInstance.isSuspended();
// 挂起--》激活
runtimeService.activateProcessInstanceById(id);
// 激活--》挂起
runtimeService.suspendProcessInstanceById(id);
TaskService
Activiti的任务管理类。可以从这个类中获取任务的信息。
// 任务查询
//需要获取一个 TaskService 对象
TaskService taskService = engine.getTaskService();
// 根据流程的key和任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("evection")
.taskAssignee(assignee).list();
// 可以拾取任务
taskService.claim(taskId,userId);
// 如果设置为null,归还组任务,任务没有负责人
taskService.setAssignee(taskId,null);
// 设置该任务的新的负责人
taskService.setAssignee(taskId,"赵六");
// 完成任务
taskService.complete(task.getId());
HistoryService
Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程
实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据。
//流程历史信息查看
// 查看历史信息我们需要通过 HistoryService来实现
HistoryService historyService = engine.getHistoryService();
// 获取 actinst 表的查询对象
HistoricActivityInstanceQuery instanceQuery =
historyService.createHistoricActivityInstanceQuery();
instanceQuery.processDefinitionId("evection:1:12504");
instanceQuery.orderByHistoricActivityInstanceStartTime().desc();
List<HistoricActivityInstance> list = instanceQuery.list();
ManagementService
Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护。
4.Activity使用
部署 activiti
Activiti 是一个工作流引擎(其实就是一堆 jar 包 API),业务系统使用 activiti 来对系统的业务流程进行自动化管理,为了方便业务系统访问(操作)activiti 的接口或功能,通常将activiti 环境与业务系统的环境集成在一起。
流程定义
使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件) 。
.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。如果使用其它公司开发的工作作引擎一般都提供了可视化的建模工具(Process Designer)用于生成流程定义文件,建模工具操作直观,一般都支持图形化拖拽方式、多窗口的用户界面、丰富的过程图形元素、过程元素拷贝、粘贴、删除等功能。 向 activiti 部署业务流程定义(.bpmn 文件)。使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件(一般情况还需要一块儿部署业务流程的图片.png)) 启动一个流程实例(ProcessInstance)
启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个 java 类,实例化两个对象一样,部署的流程就好比 java 类,启动一个流程实例就好比 new 一个 java 对象。
用户查询待办任务(Task)
因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti帮我们管理了,而不像上边需要我们在 sql语句中的where条件中指定当前查询的状态值是多少。
用户办理任务
用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了。
流程结束
当任务办理完成没有下一个任务/结点了,这个流程实例就完成了。
5.BPMN属性设置
5.1任务的执行人
(1)Assignee 指定负责人 张三
(2)Candidate Users 指定候选人 张三,李四,王五....
(3)Candidate Groups指定候选组 activitiTeam,otherTeam
5.2监听事件
(1)Task Listeners任务监听
create:任务创建后触发
assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件都触发
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
if("创建请假单".equals(delegateTask.getName())
&& "create".equals(delegateTask.getEventName())){
// 指定任务的负责人
delegateTask.setAssignee("张三-Listener");
}
}
(2)Execution Listeners 流程监听
start:流程开始出发
end:流程结束触发
public class MyTaskListener implements ExecutionListener {
@Override
public void notify(DelegateExecution delegateExecution{
//打印流程实例id
System.out.println(delegateExecution.getProcessInstanceId) }
}
5.32 表达式分配
在Activiti中支持使用UEL表达式,UEL表达式是Java EE6 规范的一部分, UEL(Unified ExpressionLanguage) 即 统一表达式语音, Activiti支持两种UEL表达式: UEL-value(${assignee}) 和UEL-method(${UserBean.getUserId()})
其他${order.price > 100 && order.price < 250}
6.流程变量
流程运转有时需要靠流程变量,业务系统和 activiti结合时少不了流程变量,流程变量就是 activiti 在管理工作流时根据管理需要而设置的变量。
6.1流程变量作用域
流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)
globa变量
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量
注意:
如: Global变量:userId(变量名)、zhangsan(变量值)global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
local变量
任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local变量名也可以和 global 变量名相同,没有影响。
6.2流程变量的使用
在启动流程时设置流程变量,变量的作用域是整个流程实例。
Map<String,Object> variables = new HashMap<>();
variables.put("assignee0","张三1");
variables.put("assignee1","李四1");
variables.put("assignee2","王五1");
variables.put("assignee3","赵财务1");
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey(key, variables);
在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。
Map<String,Object> variables = new HashMap<>();
variables.put("num",8);
taskService.complete(task.getId(),variables);
通过流程实例id设置全局变量,该流程实例必须未执行完成。也可以通
runtimeService.getVariable()获取流程变量。
runtimeService.setVariable(executionId, "evection", evection);
// 一次设置多个值
//Map<String,Object> variables = new HashMap<>();
//variables.put("num",8);
// runtimeService.setVariables(executionId, variables)
当前任务设置任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,会报错也可以通过taskService.getVariable()获取流程变量。
设置local流程变量
taskService.setVariablesLocal(taskId, variables);
7.网关
(1)排他网关 (x)
——只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为-
true时,继续执行当前网关的输出流;
如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有
true,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默
认顺序流。
(2)并行网关 (+)
——所有路径会被同时选择
拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下
执行。
(3)包容网关 (+)
—— 可以同时执行多条线路,也可以在网关上设置条件
拆分 —— 计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。