最后
学习视频:
大厂面试真题:
3.3.1、globa变量
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量
注意:
如: Global变量:userId(变量名)、zhangsan(变量值)
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
3.3.2、local变量
任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。
3.4、流程变量的使用方法
3.4.1、在属性上使用UEL表达式
可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。
Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配
3.4.2、在连线上使用UEL表达式
可以在连线上设置UEL表达式,决定流程走向。比如:${price<10000} 。price就是一个流程变量名称,uel表达式结果类型为布尔类型。如果UEL表达式是true,要决定 流程执行走向。
3.5 流程变量使用
3.5.1 需求
员工创建出差申请单,由部门经理审核,部门经理申请通过后3天以下由财务直接申批,3天以上先由总经理审批,总经理审批通过后再由财务审批。
3.5.2 流程定义
先通过UEL-value来设置负责人
然后在分支线上来设置条件
那么还可以通过对象参数命名,比如 evection.num:
另一根线对应的设置
然后可以将相关的资源文件拷贝到项目中,
3.5.3 使用Global变量
接下来使用Global变量控制流程
3.5.3.1 POJO创建
首先创建POJO对象
/**
- 出差申请的POJO对象
*/
@Data
public class Evection {
private long id;
private String evectionName;
/**
- 出差的天数
*/
private double num;
private Date beginDate;
private Date endDate;
private String destination;
private String reson;
}
3.5.3.2 流程的部署
/**
- 部署流程
*/
@Test
public void test01(){
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RepositoryService进行部署操作
RepositoryService service = engine.getRepositoryService();
// 3.使用RepositoryService进行部署操作
Deployment deploy = service.createDeployment()
.addClasspathResource(“bpmn/evection-variable.bpmn”) // 添加bpmn资源
.addClasspathResource(“bpmn/evection-variable.png”) // 添加png资源
.name(“出差申请流程-流程变量”)
.deploy();// 部署流程
// 4.输出流程部署的信息
System.out.println(“流程部署的id:” + deploy.getId());
System.out.println(“流程部署的名称:” + deploy.getName());
}
3.5.3.3 设置流程变量
a.启动时设置流程变量
在启动流程时设置流程变量,变量的作用域是整个流程实例。
/**
- 启动流程实例,设置流程变量
*/
@Test
public void test02(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 流程定义key
String key = “evection-variable”;
// 创建变量集合
Map<String,Object> variables = new HashMap<>();
// 创建出差对象 POJO
Evection evection = new Evection();
// 设置出差天数
evection.setNum(4d);
// 定义流程变量到集合中
variables.put(“evection”,evection);
// 设置assignee的取值
variables.put(“assignee0”,“张三1”);
variables.put(“assignee1”,“李四1”);
variables.put(“assignee2”,“王五1”);
variables.put(“assignee3”,“赵财务1”);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
// 输出信息
System.out.println(“获取流程实例名称:”+processInstance.getName());
System.out.println(“流程定义ID:” + processInstance.getProcessDefinitionId());
}
完成任务
/**
- 完成任务
*/
@Test
public void test03(){
String key = “evection-variable”;
String assignee = “李四1”;
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
if(task != null){
taskService.complete(task.getId());
System.out.println(“任务执行完成…”);
}
}
通过startProcessInstanceByKey方法设置流程变量的作用域是一个流程实例,流程变量使用Map存储,同一个流程实例map中的key相同,后者会覆盖前者
b.任务办理时设置
在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。
这里需要在创建出差单任务完成时设置流程变量
/**
- 启动流程实例,设置流程变量
*/
@Test
public void test02(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 流程定义key
String key = “evection-variable”;
// 创建变量集合
Map<String,Object> variables = new HashMap<>();
// 设置assignee的取值
variables.put(“assignee0”,“张三1”);
variables.put(“assignee1”,“李四1”);
variables.put(“assignee2”,“王五1”);
variables.put(“assignee3”,“赵财务1”);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
// 输出信息
System.out.println(“获取流程实例名称:”+processInstance.getName());
System.out.println(“流程定义ID:” + processInstance.getProcessDefinitionId());
}
/**
- 完成任务
*/
@Test
public void test03(){
String key = “evection-variable”;
String assignee = “李四1”;
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
Map<String,Object> variables = new HashMap<>();
// 创建出差对象 POJO
Evection evection = new Evection();
// 设置出差天数
evection.setNum(4d);
// 定义流程变量到集合中
variables.put(“evection”,evection);
if(task != null){
taskService.complete(task.getId(),variables);
System.out.println(“任务执行完成…”);
}
}
说明:
通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。
任务办理时也是通过map<key,value>设置流程变量,一次可以设置多个变量。
c.当前流程实例设置
通过流程实例id设置全局变量,该流程实例必须未执行完成。
@Test
public void setGlobalVariableByExecutionId(){
// 当前流程实例执行 id,通常设置为当前执行的流程实例
String executionId=“2601”;
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 创建出差pojo对象
Evection evection = new Evection();
// 设置天数
evection.setNum(3d);
// 通过流程实例 id设置流程变量
runtimeService.setVariable(executionId, “evection”, evection);
// 一次设置多个值
// runtimeService.setVariables(executionId, variables)
}
注意:
executionId必须当前未结束 流程实例的执行id,通常此id设置流程实例 的id。也可以通runtimeService.getVariable()获取流程变量。
d.当前任务设置
@Test
public void setGlobalVariableByTaskId(){
//当前待办任务id
String taskId=“1404”;
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
Evection evection = new Evection();
evection.setNum(3);
//通过任务设置流程变量
taskService.setVariable(taskId, “evection”, evection);
//一次设置多个值
//taskService.setVariables(taskId, variables)
}
注意:
任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,会报错也可以通过taskService.getVariable()获取流程变量。
3.5.4 设置local流程变量
3.5.4.1、任务办理时设置
任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。
/*
*处理任务时设置local流程变量
*/
@Test
public void completTask() {
//任务id
String taskId = “1404”;
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
// 定义流程变量
Map<String, Object> variables = new HashMap<String, Object>();
Evection evection = new Evection ();
evection.setNum(3d);
// 定义流程变量
Map<String, Object> variables = new HashMap<String, Object>();
// 变量名是holiday,变量值是holiday对象
variables.put(“evection”, evection);
// 设置local变量,作用域为该任务
taskService.setVariablesLocal(taskId, variables);
// 完成任务
taskService.complete(taskId);
}
说明:
设置作用域为任务的local变量,每个任务可以设置同名的变量,互不影响。
3.5.4.2、通过当前任务设置
@Test
public void setLocalVariableByTaskId(){
// 当前待办任务id
String taskId=“1404”;
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
Evection evection = new Evection ();
evection.setNum(3d);
// 通过任务设置流程变量
taskService.setVariableLocal(taskId, “evection”, evection);
// 一次设置多个值
//taskService.setVariablesLocal(taskId, variables)
}
注意:
任务id必须是当前待办任务id,act_ru_task中存在。
3.5.4.3、 Local变量测试1
如果上边例子中设置global变量改为设置local变量是否可行?为什么?
Local变量在任务结束后无法在当前流程实例执行中使用,如果后续的流程执行需要用到此变量则会报错。
3.5.4.4、 Local变量测试2
在部门经理审核、总经理审核、财务审核时设置local变量,可通过historyService查询每个历史任务时将流程变量的值也查询出来。
代码如下:
// 创建历史任务查询对象
HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
// 查询结果包括 local变量
historicTaskInstanceQuery.includeTaskLocalVariables();
for (HistoricTaskInstance historicTaskInstance : list) {
System.out.println(“==============================”);
System.out.println(“任务id:” + historicTaskInstance.getId());
System.out.println(“任务名称:” + historicTaskInstance.getName());
System.out.println(“任务负责人:” + historicTaskInstance.getAssignee());
System.out.println(“任务local变量:”+ historicTaskInstance.getTaskLocalVariables());
}
注意:查询历史流程变量,特别是查询pojo变量需要经过反序列化,不推荐使用。
4.1、需求
在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。
针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。
4.2、设置任务候选人
在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。
查看bpmn文件
我们可以看到部门经理的审核人已经设置为 lisi,wangwu 这样的一组候选人,可以使用activiti:candiateUsers=”用户 1,用户 2,用户 3”的这种方式来实现设置一组候选人
4.3、组任务
4.3.1、组任务办理流程
a、查询组任务
指定候选人,查询该候选人当前的待办任务。候选人不能立即办理任务。
b、拾取(claim)任务
该组任务的所有候选人都能拾取。将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。如果拾取后不想办理该任务?需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
c、查询个人任务
查询方式同个人任务部分,根据assignee查询用户负责的个人任务。
d、办理个人任务
4.3.2、 查询组任务
根据候选人查询组任务
/**
- 查询组任务
*/
@Test
public void test03(){
String key = “evection1”;
String candidateUser = “lisi”;
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List list = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskCandidateUser(candidateUser)
.list();
for (Task task : list) {
System.out.println(“流程实例Id:” + task.getProcessInstanceId());
System.out.println(“任务ID:” + task.getId());
System.out.println(“负责人:” + task.getAssignee());
System.out.println(“任务名称:” + task.getName());
}
}
4.3.3 、 拾取组任务
候选人员拾取组任务后该任务变为自己的个人任务。
/**
- 候选人 拾取任务
*/
@Test
public void test04(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
String taskId = “72505”;
// 候选人
String userId = “lisi”;
// 拾取任务
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskCandidateUser(userId) // 根据候选人查询
.singleResult();
if(task != null){
// 可以拾取任务
taskService.claim(taskId,userId);
System.out.println(“拾取成功”);
}
}
4.3.4、 查询个人待办任务
查询方式同个人任务查询
@Test
public void test03(){
String key = “evection1”;
String candidateUser = “lisi”;
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List list = taskService.createTaskQuery()
.processDefinitionKey(key)
//.taskCandidateUser(candidateUser)
//.taskCandidateOrAssigned(candidateUser)
.taskAssignee(candidateUser)
.list();
for (Task task : list) {
System.out.println(“流程实例Id:” + task.getProcessInstanceId());
System.out.println(“任务ID:” + task.getId());
System.out.println(“负责人:” + task.getAssignee());
System.out.println(“任务名称:” + task.getName());
}
}
4.3.5、 办理个人任务
同个人任务办理
/**
- 完成个人任务
*/
@Test
public void test05(){
String taskId = “72505”;
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
taskService.complete(taskId);
System.out.println(“完成任务:” + taskId);
}
4.3.6、 归还组任务
如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人
/**
- 归还任务
*/
@Test
public void test06(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
String taskId = “75002”;
String userId= “zhangsan”;
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(userId)
.singleResult();
if(task != null){
// 如果设置为null,归还组任务,任务没有负责人
taskService.setAssignee(taskId,null);
}
}
4,3,7 任务交接
任务负责人将任务交给其他负责人来处理
/**
- 任务交接
*/
@Test
public void test07(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
String taskId = “75002”;
String userId= “zhangsan”;
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(userId)
.singleResult();
if(task != null){
// 设置该任务的新的负责人
taskService.setAssignee(taskId,“赵六”);
}
}
4.3.8、 数据库表操作
查询当前任务执行表
SELECT * FROM act_ru_task
任务执行表,记录当前执行的任务,由于该任务当前是组任务,所有assignee为空,当拾取任务后该字段就是拾取用户的id,查询任务参与者
SELECT * FROM act_ru_identitylink
任务参与者,记录当前参考任务用户或组,当前任务如果设置了候选人,会向该表插入候选人记录,有几个候选就插入几个与act_ru_identitylink对应的还有一张历史表act_hi_identitylink,向act_ru_identitylink插入记录的同时也会向历史表插入记录。任务完成
网关用来控制流程的流向
5.1 排他网关ExclusiveGateway
5.1.1 什么是排他网关:
排他网关,用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支,
注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。
为什么要用排他网关?
不用排他网关也可以实现分支,如:在连线的condition条件上设置分支条件。在连线设置condition条件的缺点:如果条件都不满足,流程就结束了(是异常结束)。如果 使用排他网关决定分支的走向,如下:
如果从网关出去的线所有条件都不满足则系统抛出异常。
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway ‘exclusivegateway1’ could be selected for continuing the process
at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)
5.1.2 流程定义
排他网关图标,红框内:
5.1.3 测试
在部门经理审核后,走排他网关,从排他网关出来的分支有两条,一条是判断出差天数是否大于3天,另一条是判断出差天数是否小于等于3天。设置分支条件时,如果所有分支条件都不是true,报错:
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway ‘exclusivegateway1’ could be selected for continuing the process
at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)
5.2 并行网关ParallelGateway
5.2.1 什么是并行网关
并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
l fork分支:
并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
l join汇聚:
所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
例子:
说明:
技术经理和项目经理是两个execution分支,在act_ru_execution表有两条记录分别是技术经理和项目经理,act_ru_execution还有一条记录表示该流程实例。待技术经理和项目经理任务全部完成,在汇聚点汇聚,通过parallelGateway并行网关。并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。
5.2.2 流程定义
并行网关图标,红框内:
5.2.3 测试
当执行到并行网关数据库跟踪如下:当前任务表:SELECT * FROM act_ru_task
上图中:有两个任务当前执行。查询流程实例执行表:SELECT * FROM act_ru_execution
上图中,说明当前流程实例有多个分支(两个)在运行。
对并行任务的执行:
并行任务执行不分前后,由任务的负责人去执行即可。
执行技术经理任务后,查询当前任务表 SELECT * FROM act_ru_task
已完成的技术经理任务在当前任务表act_ru_task_已被删除。
在流程实例执行表:SELECT * FROM act_ru_execution有中多个分支存在且有并行网关的汇聚结点。
有并行网关的汇聚结点:说明有一个分支已经到汇聚,等待其它的分支到达。
当所有分支任务都完成,都到达汇聚结点后:
流程实例执行表:SELECT * FROM act_ru_execution,执行流程实例已经变为总经理审批,说明流程执行已经通过并行网关
总结:所有分支到达汇聚结点,并行网关执行完成。
5.3 包含网关InclusiveGateway
5.3.1 什么是包含网关
包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的:
l 分支:
所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。
l 汇聚:
所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。
5.3.2 流程定义:
出差申请大于等于3天需要由项目经理审批,小于3天由技术经理审批,出差申请必须经过人事经理审批。
包含网关图标,红框内:
定义流程:
注意:通过包含网关的每个分支的连线上设置condition条件。
5.3.3 测试
如果包含网关设置的条件中,流程变量不存在,报错;
org.activiti.engine.ActivitiException: Unknown property used in expression: ${evection.num>=3}
需要在流程启动时设置流程变量evection.num。
1)、当流程执行到第一个包含网关后,会根据条件判断,当前要走哪几个分支:
流程实例执行表:SELECT * FROM act_ru_execution
第一条记录:包含网关分支。
后两条记录代表两个要执行的分支:
ACT_ID = “_13” 代表 项目经理神品
ACT_ID = “_5” 代表 人事经理审批
当前任务表:ACT_RU_TASK
总结
以上是字节二面的一些问题,面完之后其实挺后悔的,没有提前把各个知识点都复习到位。现在重新好好复习手上的面试大全资料(含JAVA、MySQL、算法、Redis、JVM、架构、中间件、RabbitMQ、设计模式、Spring等),现在起闭关修炼半个月,争取早日上岸!!!
下面给大家分享下我的面试大全资料
- 第一份是我的后端JAVA面试大全
后端JAVA面试大全
- 第二份是MySQL+Redis学习笔记+算法+JVM+JAVA核心知识整理
MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理
- 第三份是Spring全家桶资料
MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理
img-blog.csdnimg.cn/20210611204023833.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NTI2NTcz,size_16,color_FFFFFF,t_70)
注意:通过包含网关的每个分支的连线上设置condition条件。
5.3.3 测试
如果包含网关设置的条件中,流程变量不存在,报错;
org.activiti.engine.ActivitiException: Unknown property used in expression: ${evection.num>=3}
需要在流程启动时设置流程变量evection.num。
1)、当流程执行到第一个包含网关后,会根据条件判断,当前要走哪几个分支:
流程实例执行表:SELECT * FROM act_ru_execution
第一条记录:包含网关分支。
后两条记录代表两个要执行的分支:
ACT_ID = “_13” 代表 项目经理神品
ACT_ID = “_5” 代表 人事经理审批
当前任务表:ACT_RU_TASK
总结
以上是字节二面的一些问题,面完之后其实挺后悔的,没有提前把各个知识点都复习到位。现在重新好好复习手上的面试大全资料(含JAVA、MySQL、算法、Redis、JVM、架构、中间件、RabbitMQ、设计模式、Spring等),现在起闭关修炼半个月,争取早日上岸!!!
下面给大家分享下我的面试大全资料
- 第一份是我的后端JAVA面试大全
[外链图片转存中…(img-0CcVo1ik-1715364263550)]
后端JAVA面试大全
- 第二份是MySQL+Redis学习笔记+算法+JVM+JAVA核心知识整理
[外链图片转存中…(img-arnmY0Fa-1715364263551)]
MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理
- 第三份是Spring全家桶资料
[外链图片转存中…(img-uXvPVO8d-1715364263552)]
MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理