Spring Boot 项目集成camunda流程引擎
Spring Boot 项目集成camunda流程引擎
camunda地址
camunda中文地址
使用camunda开源工作流引擎有:通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。
文本重点介绍如何在Spring Boot应用程序中如何集成Camunda Platform开源流程平台,这也是项目中最为常见的一种使用方式。
在本教程中,我们假设您熟悉 Java Web 应用程序开发和 Spring Boot 的基础知识。前提条件是您已经安装了 Eclipse/IDEA等Java开发工具和 Camunda Modeler流程设计器。
1.新建Spring Boot 项目集成camunda
首先,让我们在您选择的 IDE 中设置您的第一个流程应用程序项目。
该项目需要 Java jdk8以上版本。我本地使用的JDK版本为8,使用的开发工具IDEA2024。
1.1. 添加 Camunda 平台和 Spring Boot 依赖项
下一步包括为新项目设置 Maven 依赖项。需要将 Maven 依赖添加到项目的文件中。由于本示例要使用camunda流程引擎、web界面、Rest服务接口,所以需要导入camunda-bpm-spring-boot-starter-rest、camunda-bpm-spring-boot-starter-webapp依赖包。我们在“依赖管理”部分添加了 Spring Boot BOM和camunda相关依赖,这将自动将 camunda 引擎、rest服务接口和 Web 应用程序包含在应用程序中。
我们使用camunda7.19.0版本,该版本支持jdk8和springboot2。camunda和springboot版本的依赖对应关系,查看官方文档说明:Spring Boot Version Compatibility | docs.camunda.org
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>camunda-demo</artifactId><name>camunda-demo</name><groupId>com.liuhm</groupId><version>1.0</version><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><spring-boot.version>2.7.5</spring-boot.version><camunda.spring-boot.version>7.19.0</camunda.spring-boot.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId><version>${camunda.spring-boot.version}</version></dependency><dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boot-starter-rest</artifactId><version>${camunda.spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.6</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version></plugin><!--指定JDK编译版本 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build></project>
1.2. 配置 Spring Boot 项目
在项目中src/main/resources新建application.yml
让我们在文件夹中创建一个包含以下内容的文件:application.yml
默认在mysql数据库中创建了camundatest
spring:datasource:url: jdbc:mysql://192.168.0.154:3306/camundatest?characterEncoding=UTF-8&useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Drivercamunda:bpm:database:type: mysqlschema-update: true # 是否自动建表,但我测试为true时,创建表会出现,因此还是改成false由手工建表。auto-deployment-enabled: false # 自动部署 resources 下的 bpmn文件admin-user:id: adminpassword: 123456
server:port: 8080
此配置将导致以下结果:
- 将创建具有提供的密码和名字的管理员用户 admin/123456。
- 认使用mysql数据库,启动时自动创建数据库。
1.3. 编写Spring Boot启动类
接下来,我们添加一个带有 main 方法的应用程序类,该方法将成为启动 Spring Boot 应用程序的入口点。该类上有@SpringBootApplication注解,它隐含地添加了几个方便的功能(自动配置、组件扫描等 - 参见 Spring Boot 文档)
package com.liuhm;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@Slf4j
public class CamundaApplication {public static void main(String[] args) {try {SpringApplication.run(CamundaApplication.class, args);}catch (Exception e){log.error(ExceptionUtils.getStackTrace(e));}}
}
1.4. 启动Spring Boot工程
在IDEA的maven操作窗口,执行mvn clean install命令,下载相关的第三方Jar包。
1.4.1. 启动
我们的第一个 Camunda Spring Boot 应用程序现已准备就绪,此程序是一个 Spring Boot 应用程序,它作为 Web 容器、Camunda 引擎和 Camunda Web 应用程序资源嵌入到 Tomcat 中,并使用了mysql 数据库。您可以通过右键单击该类并选择CamundaApplication来运行应用程序。
1.4.2. 访问
现在,在浏览器中打开 http://localhost:8080/ 时,您可以使用我们之前配置的登录名和密码“admin/123456”来访问 Camunda Web 应用程序。
能正常登录访问这个界面,表示基于springboot集成camunda成功了。
1.4.3. 数据库表
数据库也创建了表
执行完成后,通过工具打开数据库控制台查看,一共有49张表。
执行的sql脚本,找到camunda-bpm-platform-7.19.0\engine\src\main\resources\org\camunda\bpm\engine\db\create文件夹下的数据库脚本,选择mysql脚本依次执行即可。
2.设计并部署一个BPMN流程
我们将学习如何使用camunda流程设计器设计一个BPMN2的业务流程,并部署流程。
2.1.下载安装流程设计器
官网下载:Camunda Modeler官网下载(https://camunda.com/download/modeler/)
直接下载地址:https://downloads.camunda.cloud/release/camunda-modeler/5.29.0/camunda-modeler-5.29.0-win-x64.zip
下载 流程设计器后camunda-modeler后,只需将下载内容解压缩到您选择的文件夹中即可。
成功解压缩后,对于 Windows 用户运行Camunda Modeler.exe,对于 Mac 用户或 Linux 用户运行.sh文件,启动流程建模器。
2.2. 设计BPMN流程
首先使用 Camunda Modeler 对可执行流程进行建模。设置两个人工任务节点,配置流程处理人为admin用户。
流程模型bpmn内容:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0p1akz6" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.29.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.22.0"><bpmn:process id="test1" isExecutable="true"><bpmn:startEvent id="StartEvent_1"><bpmn:outgoing>Flow_1747ke5</bpmn:outgoing></bpmn:startEvent><bpmn:sequenceFlow id="Flow_1747ke5" sourceRef="StartEvent_1" targetRef="Activity_14mnow7" /><bpmn:userTask id="Activity_14mnow7" name="申请" camunda:assignee="admin"><bpmn:incoming>Flow_1747ke5</bpmn:incoming><bpmn:outgoing>Flow_0t3binq</bpmn:outgoing></bpmn:userTask><bpmn:sequenceFlow id="Flow_0t3binq" sourceRef="Activity_14mnow7" targetRef="Activity_1ezp043" /><bpmn:userTask id="Activity_1ezp043" name="审批" camunda:assignee="admin"><bpmn:incoming>Flow_0t3binq</bpmn:incoming><bpmn:outgoing>Flow_0ug1mkb</bpmn:outgoing></bpmn:userTask><bpmn:endEvent id="Event_110i53j"><bpmn:incoming>Flow_0ug1mkb</bpmn:incoming></bpmn:endEvent><bpmn:sequenceFlow id="Flow_0ug1mkb" sourceRef="Activity_1ezp043" targetRef="Event_110i53j" /></bpmn:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="test1"><bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1"><dc:Bounds x="192" y="82" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_106g3s5_di" bpmnElement="Activity_14mnow7"><dc:Bounds x="160" y="170" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1afp88w_di" bpmnElement="Activity_1ezp043"><dc:Bounds x="160" y="280" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_110i53j_di" bpmnElement="Event_110i53j"><dc:Bounds x="192" y="412" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNEdge id="Flow_1747ke5_di" bpmnElement="Flow_1747ke5"><di:waypoint x="210" y="118" /><di:waypoint x="210" y="170" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0t3binq_di" bpmnElement="Flow_0t3binq"><di:waypoint x="210" y="250" /><di:waypoint x="210" y="280" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0ug1mkb_di" bpmnElement="Flow_0ug1mkb"><di:waypoint x="210" y="360" /><di:waypoint x="210" y="412" /></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</bpmn:definitions>
2.3. 发布BPMN流程
点击流程设计器左下方的发布流程按钮:
3. 验证camunda流程引擎
3.1. 通过camunda web控制台测试
现在,当您在浏览器中打开 http://localhost:8080/camunda/app/tasklist/ 时,您可以使用我们之前配置的登录名和密码“admin/123456”来访问 Camunda Web 应用程序。
选择刚刚设计的的流程“test1”,发起一个流程实例。点击左侧“Add a simple filter”添加一个默认待办任务过滤器,就可以查看到刚刚提交的流程待办任务了。
此时,我看打开mysql数据库表,查看camunda数据库表里的数据:
3.1.1. ACT_RE_DEPLOYMENT
打开流程定义发布表ACT_RE_DEPLOYMENT,看到我们刚刚发布的这个流动定义模型。
3.1.2. ACT_HI_PROCINST
打开流程实例历史表ACT_HI_PROCINST,看到我们刚刚发起的这个流程实例数据。
3.1.3. ACT_RU_TASK
打开流程待办任务表ACT_RU_TASK,多了一条demo用户待处理的任务。
3.2. 通过camunda rest接口测试
以上我们通过camunda的web界面进行了发起流程测试验证,下面我们通过Camunda REST API的方式进行测试验证。
Camunda Platform REST API官方说明文档:Camunda Platform REST API
3.2.1.查询流程定义
查看流程定义rest接口:http://{host}:{port}/{contextPath}/process-definition
详细接口描述见官方文档:Camunda Platform REST API
用Postman测试验证:http://localhost:8080/engine-rest/process-definition
返回json:
[{"id": "test1:1:7d603a61-abc5-11ef-9172-0a0027000009","key": "test1","category": "http://bpmn.io/schema/bpmn","description": null,"name": null,"version": 1,"resource": "diagram_1.bpmn","deploymentId": "7d4e600f-abc5-11ef-9172-0a0027000009","diagram": null,"suspended": false,"tenantId": null,"versionTag": null,"historyTimeToLive": null,"startableInTasklist": true}
]
3.2.2. 发起流程实例
流程发起的rest接口为:http://{host}:{port}/{contextPath}/process-definition/key/{key}/start
详细接口描述见官方文档:Camunda Platform REST API
打开postman工具进行测试验证:http://localhost:8080/engine-rest/process-definition/key/test1/start
输入json
{"variables": {"variable1": {"value": "hello","type": "String"},"variable2": {"value": true,"type": "Boolean"}},"businessKey": "第二次发起请求"
}
返回json
{"links": [{"method": "GET","href": "http://localhost:8080/engine-rest/process-instance/2f8a9efb-abc7-11ef-9172-0a0027000009","rel": "self"}],"id": "2f8a9efb-abc7-11ef-9172-0a0027000009","definitionId": "test1:1:7d603a61-abc5-11ef-9172-0a0027000009","businessKey": "第二次发起请求","caseInstanceId": null,"ended": false,"suspended": false,"tenantId": null
}
3.2.3. 查询待办任务
通过上面接口得知,流程当前流转到了人工节点上,那么需要查询待办任务:
查询待办任务的rest接口:http://{host}:{port}/{contextPath}/task
详细接口描述见官方文档:Camunda Platform REST API
用Postman测试:http://localhost:8080/engine-rest/task
返回json
[{"id": "2f8bfe92-abc7-11ef-9172-0a0027000009","name": "申请","assignee": "admin","created": "2024-11-26T15:22:26.000+0800","due": null,"followUp": null,"lastUpdated": null,"delegationState": null,"description": null,"executionId": "2f8a9efb-abc7-11ef-9172-0a0027000009","owner": null,"parentTaskId": null,"priority": 50,"processDefinitionId": "test1:1:7d603a61-abc5-11ef-9172-0a0027000009","processInstanceId": "2f8a9efb-abc7-11ef-9172-0a0027000009","taskDefinitionKey": "Activity_14mnow7","caseExecutionId": null,"caseInstanceId": null,"caseDefinitionId": null,"suspended": false,"formKey": null,"camundaFormRef": null,"tenantId": null}
]
3.2.4. 完成待办提交流程
完成待办任务,提交流程往下走,提交流程的rest服务接口为:
流程发起的rest接口为:http://{host}:{port}/{contextPath}/task/{id}/complete
详细接口描述见官方文档:Camunda Platform REST API
用Postman测试:http://localhost:8080/engine-rest/task/2f8bfe92-abc7-11ef-9172-0a0027000009/complete
输入json:
{"variables": {"variable": {"value": "china"},"variable2": {"value": false}},"withVariablesInReturn": true
}
返回json:
{"variable1": {"type": "String","value": "hello","valueInfo": {}},"variable2": {"type": "Boolean","value": false,"valueInfo": {}},"variable": {"type": "String","value": "china","valueInfo": {}}
}
3.3. 通过Java API接口测试
上面介绍了通过Camunda Web控制台界面和Camunda提供的rest接口两种方式,来调用流程引擎服务。以下介绍第三种方式,即Java编码方式,直接调用Camunda提供的Service接口。
我们自己开发一个RestController服务类,类里注入RuntimeService和TaskService的SpringBean,然后调用这两个类的API接口,实现发起流程和查询待办的逻辑。
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;@RestController
@RequestMapping
public class TestController {@Resourceprivate RuntimeService runtimeService;@Resourceprivate TaskService taskService;/*** 通过流程定义key,发起一个流程实例* @param processKey 流程定义key* @return 流程实例ID*/@GetMapping(value = "/startProcessInstanceByKey/{processKey}")public String startProcessInstanceByKey(@PathVariable("processKey") String processKey) {ProcessInstance instance = runtimeService.startProcessInstanceByKey(processKey);return instance.getRootProcessInstanceId();}/*** 查询某个用户的待办任务* @param assignee 用户ID* @return 待办任务列表*/@GetMapping(value = "/getTaskByAssignee/{assignee}")public String getTaskByAssignee(@PathVariable("assignee") String assignee) {List<TaskEntity> taskList = (List)taskService.createTaskQuery().taskAssignee(assignee).list();StringBuffer sb = new StringBuffer();for (Task task : taskList) {String taskTitle = "待办任务ID="+task.getId()+",流程实例ID="+task.getProcessInstanceId()+"\n";System.out.println(taskTitle);sb.append(taskTitle);}return sb.toString();}
}
重启启动Springboot程序,调用刚刚开发的流程接口进行测试。
发起一个流程实例:http://localhost:8080/startProcessInstanceByKey/test1
执行成功,返回了流程实例ID,接着查询用户admin的待办任务:
http://localhost:8080/getTaskByAssignee/admin
4. 绘制流程图介绍
4.1. 绘制
新建一个
我这边稍微画了一个,具体怎么画,就不在细说了,最后效果如下,模拟了个OA的流程
4.2. 任务分类
只介绍最常用的两种
- 用户任务 (User Task)
具体来说就是需要手动执行的任务,即需要我们这变写完业务代码后,调用代码
taskService.complete(taskId, variables);
才会完成的任务
- 系统任务(Service Task)
系统会自动帮我们完成的任务
4.3. 网关
分为这么几类,会根据我们传入的流程变量及设定的条件走
- 排他网关(exclusive gateway)
这个网关只会走一个,我们走到这个网关时,会从上到下找第一个符合条件的任务往下走
- 并行网关(Parallel Gateway)
这个网关不需要设置条件,会走所有的任务
- 包含网关(Inclusive Gateway)
这个网关会走一个或者多个符合条件的任务
示例
如上图包含网关,需要在网关的连线初设置表达式 condition,
参数来自于流程变量
两个参数:
switch2d 、 switch3d
如果 都为true,则走任务1,3
如果 switch2d 为true switch3d为false,则只走任务1
如果 switch3d 为true switch2d为false,则只走任务3
如果都为false,则直接走网关,然后结束
5. 任务相关API介绍
基于service的查询类,都可先构建一个 query,然后在附上查询条件,实例几个
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().listPage(1, 10);
5.1. 查询历史任务
List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
5.2. 查询当前任务/分页
List<Task> list = taskService.createTaskQuery().orderByTaskCreateTime().desc().list();
5.3. 任务回退
大体思路是拿到当前的任务,及当前任务的上一个历史任务,然后重启
代码示例
Task activeTask = taskService.createTaskQuery().taskId(taskId).active().singleResult();
List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().desc().list();List<HistoricTaskInstance> historicTaskInstances = historicTaskInstance.stream().filter(v -> !v.getTaskDefinitionKey().equals(activeTask.getTaskDefinitionKey())).toList();Assert.notEmpty(historicTaskInstances, "当前已是初始任务!");
HistoricTaskInstance curr = historicTaskInstances.get(0);runtimeService.createProcessInstanceModification(instanceId).cancelAllForActivity(activeTask.getTaskDefinitionKey()).setAnnotation("重新执行").startBeforeActivity(curr.getTaskDefinitionKey()).execute();
5.4. 流程变量
包括流程中产生的变量信息,包括控制流程流转的变量,网关、业务表单中填写的流程需要用到的变量等。很多地方都要用到
流程变量变量传递
变量最终会存在 act_ru_variable
这个表里面
在绘制流程图的时候,如果是用户任务(userService
) 可以设置变量,比如执行人,
写法有这么几种方式
- 写死,就比如 zhangsan
- 表达式,比如上面写的 ${user},这种需要传入参数,其实就是启动参数的时候传入,传入参数,可选值为一个
Map<String, Object>
,之后的流程可查看次参数,上面写的是 user, 所以map里面的key需要带着user,不然会报错。
关于扩展变量,可在流程图绘制这么设定,传递方式还是一样,流程图里面在下面写:
代码:
ProcessInstance instance = runtimeService.startProcessInstanceByKey(key, new HashMap<>());
变量设置
runtimeService.setVariable(instance.getId(), Constants.PATIENT_ID, relatedId);
变量查询
Object variable = runtimeService.getVariable(instance.getId(), Constants.GENERAL_ID);
历史变量查询
HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(bo.getId().toString()).variableName(Constants.PATIENT_ID).singleResult();
//变量值
variableInstance.getValue();
//变量名称
variableInstance.getName();
5.5. 针对后端来说任务类型主要有两种。
用户任务-userTask
即需要用户参与的任务,因为工作流执行过程中需要涉及到审批、过审之类的需要用户参与的任务,这个时候需要用户参与,然后调用接口完成任务。
服务任务-serviceTask
即自动执行的任务,比如用户提交后,系统自动存储、修改状态等自动完成的任务。
Type
任务类型是关键,可根据配型配置实现调用 java的方法,spring 的bean方法,等等有这么几种类型
**Delegate Expression **推荐使用 –
在系统任务中,因为是自动执行,所以实际应用中需要嵌入各种业务逻辑,可以在流程图设计中,按照下面方式调用java代码执行,在spring中配置同名的bean
配置表达式,可以实现JavaDelegate接口使用类名配置,快捷写法如下,比较推荐下面这种,此种可灵活配置bean和spring结合使用,注入service等业务方法
@Bean("t17")
JavaDelegate t17() {return execution -> {Map<String, Object> variables = execution.getVariables();Task task = taskService.createTaskQuery().processInstanceId(execution.getProcessInstanceId()).singleResult();//业务逻辑task.setOwner(String.valueOf(dentistId));};
}
Java Class :
配置java类名,需要实现JavaDelegate接口,注意是全路径名,不可以使用Spring的bean配置!!!
@Component
public class T17Delegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) throws Exception {String taskId = execution.getId();String instanceId = execution.getProcessInstanceId();Map<String, Object> variables = execution.getVariables();}
}
下面两种可使用spring的配置
Expression:
EL表达式,调用java类的方法 ,规范:
expression="#{monitorExecution.execution(execution)}"
@Component("monitorExecution")
public class MonitorExecution {public void execution(DelegateExecution execution){String processInstanceId = execution.getProcessInstanceId();}
}
5.6.监听器
任务监听器 - Task Listener
任务监听器用于在某个与任务相关的事件发生时执行自定义Java逻辑或表达式。它只能作为用户任务的子元素添加到流程定义中。请注意,这也必须作为BPMN 2.0扩展元素的子级和Camunda命名空间中发生,因为任务侦听器是专门为Camunda引擎构建的。
适用场景:
@Bean
TaskListener t21() {return delegateTask -> {String taskId = delegateTask.getId();String instanceId = delegateTask.getProcessInstanceId();Map<String, Object> variables = delegateTask.getVariables();// TODO: 20log/3/22delegateTask.setVariable("", "");};
}
执行监听器 - Execution Listener
执行侦听器在流程执行过程中发生某些事件时执行外部Java代码或计算表达式。可以用在任何任务中,可以捕获的事件有:
- 流程实例的开始和结束。
- 进行过渡。
- 活动的开始和结束。
- 网关的开始和结束。
- 中间事件的开始和结束。
- 结束开始事件或开始结束事件
适用场景:每个任务结束时设置任务进度
public class ExampleExecutionListenerOne implements ExecutionListener {public void notify(DelegateExecution execution) throws Exception {execution.setVariable("variableSetInExecutionListener", "firstValue");execution.setVariable("eventReceived", execution.getEventName());}
}
5.7. 扩展属性- Extension properties
扩展属性适用于很多自定义的业务属性,比如设置业务流程进度
5.8. 流程权限及创建人设置
IdentityService
为鉴权相关服务,但是我们实际开发中,一般会用到我们自己的鉴权系统,所以可以使用camunda提供的api来设置,具体可以看IdentityServiceImpl
这个类,其中也是使用了ThreadLocal来保存鉴权信息 ,代码在下面
private ThreadLocal<Authentication> currentAuthentication = new ThreadLocal<Authentication>();
用户信息设置:
// Userutil是我们自己封装的用户工具类
identityService.setAuthenticatedUserId(UserUtil.getUserId().toString());//获取
Authentication authentication = identityService.getCurrentAuthentication();
他内置很多比如开启流程时候,会默认找当前登录的人,这个类DefaultHistoryEventProducer
// set super process instance id
ExecutionEntity superExecution = executionEntity.getSuperExecution();
if (superExecution != null) {evt.setSuperProcessInstanceId(superExecution.getProcessInstanceId());
}//state
evt.setState(HistoricProcessInstance.STATE_ACTIVE);// set start user Id
evt.setStartUserId(Context.getCommandContext().getAuthenticatedUserId());
5.9.任务执行人及发起人设置
//根据任务id设置执行人
taskService.setAssignee(task.getId(), UserUtil.getUserId().toString());
博客地址
代码下载
下面的camunda-demo
相关文章:
Spring Boot 项目集成camunda流程引擎
Spring Boot 项目集成camunda流程引擎 camunda地址 camunda中文地址 使用camunda开源工作流引擎有:通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 文本重点介绍如何在Spring Boot应用程序中如何集成Camunda Platform开…...
Ubuntu20.04编译安装Carla全过程
前言 Carla的安装是我现阶段解决的第一个问题,现记录一下我安装Carla的过程以及我在安装过程中遇到的一些问题。 一、安装前准备 1、硬件环境 carla是一款基于UE4开发的模拟仿真软件,本身对硬件的要求比较高。 我是windows与ubuntu双系统࿰…...
typecho 自动订阅 RSS
昨天学习了一下 RSS 订阅知识之后,经过一番百度搜索,终于在自己的博客上实现了 RSS 订阅功能,但苦于技术有限,不能对 Feed 文件进行定时缓存,每次打开链接都会比较延迟。今天继续对这个功能进行了学习,突然…...
MFC图形函数学习13——在图形界面输出文字
本篇是图形函数学习的最后一篇,相关内容暂告一段落。 在图形界面输出文字,涉及文字字体、大小、颜色、背景、显示等问题,完成这些需要系列函数的支持。下面做简要介绍。 一、输出文本函数 原型:virtual BOOL te…...
11.25.2024刷华为OD
文章目录 HJ76 尼科彻斯定理(观察题,不难)HJ77 火车进站(DFS)HJ91 走格子方法,(动态规划,递归,有代表性)HJ93 数组分组(递归)语法知识…...
Python练习55
Python日常练习 题目: 补充函数getLastDay(y,m),其功能是计算y年m月共有多少天。 --------------------------------------------------------- 注意: 部分源程序给出如下。请勿改动主函数main和其它函数中的 任何内容,…...
DDR5和DDR4之区别(The Difference between DDR5 and DDR4)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 DDR是什么意思? DDR5和D…...
分层架构 IM 系统之 Router 架构分析
通过前面文章的分析,我们已经明确,Router 的核心职责是作为中央存储记录在线客户端的连接状态,Router 在本质上是一个内存数据库。 内存是一种易失性的存储,既如此,Router 的可用性如何保障呢? 副本是分布…...
用函数实现模块化程序设计(七)--数组作为函数参数(排序算法)
调用有参函数时,需要实参,实参可以是常量,变量,表达式,数组元素的作用与变量相当,凡是变量出现的地方都可用数组代替,数组元素可以用作函数实参,数组名可以作实参和形参,…...
M31系列LoRa分布式IO模块功能简介
M31系列LoRa 分布式 IO 模块简介 M31系列LoRa分布式IO主机模块是一款强大的无线远程控制与采集设备,该设备采用 LoRa 无线技术(内置了无线模块),可通过串口或远程 LoRa 组网设备发送 Modbus RTU 指令进行控制,可搭配E…...
Dockerfile 安装echarts插件给java提供服务
java调用echarts插件,生成图片保存到磁盘然后插入到pptx中报表。 Dockerfile文件内容: #基础镜像,如果本地仓库没有,会从远程仓库拉取 openjdk:8 FROM docker.io/centos:centos7 #暴露端口 EXPOSE 9311 # 避免centos 日志输出 …...
学习threejs,使用VideoTexture实现视频Video更新纹理
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️VideoTexture 视频纹理 二、…...
【二分查找】Leetcode例题
【1】69. x 的平方根 - 力扣(LeetCode) 🍡解题思路:首先想到的是暴力查找,从1开始依次比较x与num*num的大小,然后找出满足num*num<x且(num1)*(num1)>x的num值;再来看看能不能优化一下&…...
稳定运行的以MySQL数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
在ETL(Extract, Transform, Load)过程中,数据源和目标都为MySQL数据库时,性能变差可能由多种原因引起。提高以MySQL为数据源和目标的ETL性能需要综合考虑数据库性能、ETL任务的处理方式、硬件资源和工具的选择。通过批量处理、并行…...
Springboot(四十九)SpringBoot3整合jetcache缓存
上文中我们学习了springboot中缓存的基本使用。缓存分为本地caffeine缓存和远程redis缓存。现在有一个小小的问题,我想使用本地caffeine缓存和远程redis缓存组成二级缓存。还想保证他们的一致性,这个事情该怎么办呢? Jetcache框架为我们解决了这个问题。 JetCache是一个…...
JVM 参数前缀 -XX: 含义 详解
在 Java 虚拟机(JVM)中,参数前缀 -XX: 表示的是 JVM 的非标准(实验性)选项。这些参数用于调整和优化 JVM 的性能、垃圾回收行为、内存分配策略等。 1. 参数分类 -XX: 参数大致分为三类,根据其格式区分&…...
【Mac】安装Gradle
1、说明 Gradle 运行依赖 JVM,需要先安装JDK,Gradle 与 JDK的版本对应参见:Java Compatibility IDEA的版本也是有要求Gradle版本的,二者版本对应关系参见:Third-Party Software and Licenses 本次 Gradle 安装版本为…...
证明切平面过定点的曲面是锥面
目录 证明:切平面过定点的曲面是锥面. 证明:切平面过定点的曲面是锥面. 证明: 方法一: 设曲面 S : r r ( u , v ) S:\mathbf{r}\mathbf{r}(u,v) S:rr(u,v)的切平面过定点 P 0 P_0 P0,其位置向量为 p 0 . \mathbf{p}_0. p0…...
【WPS】【EXCEL】将单元格中字符按照分隔符拆分按行填充到其他单元格
问题:实现如下图的效果 解答: 一、函数 IFERROR(TRIM(MID(SUBSTITUTE($A$2,",",REPT(" ",LEN($A$2))),(ROW(A1)-1)*LEN($A$2)1,LEN($A$2))),"") 二、在单元格C2中填写如下函数 三、全选要填充的单元格并且按CTRLD 函数…...
工作:三菱PLC防止程序存储器爆满方法
工作:三菱PLC防止程序存储器爆满方法 一、防止程序存储器爆满方法1、编程时,添加行注释时,记得要选“外围”,这样不会占用PLC程序存储器内存;2、选择“外围”的注释,前面会有个*星号,方便检查 二…...
【计算机组成原理】1位预测机制与2位预测机制详解
1位预测机制与2位预测机制详解 在计算机体系结构中,分支预测是为了提高流水线效率而对分支指令执行结果进行预测的技术。1位预测机制和2位预测机制是两种常见的分支预测策略,它们通过预测分支是否发生来决定是否跳转。 一、1位预测机制 1位预测机制是…...
基于SpringBoot+Vue的靓车汽车销售网站-无偿分享 (附源码+LW+调试)
目录 1. 项目技术 2. 功能菜单 3. 部分功能截图 4. 研究背景 5. 研究目的 6. 可行性分析 6.1 技术可行性 6.2 经济可行性 6.3 操作可行性 7. 系统设计 7.1 概述 7.2 系统流程和逻辑 7.3 系统结构 8. 数据库设计 8.1 数据库ER图 (1)材料分…...
ESP32-S3模组上跑通ES8388(13)
接前一篇文章:ESP32-S3模组上跑通ES8388(12) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回解析了es8388_init函数中的第6段代码,本回继续往下解析。为了便于理解和回顾,再次贴出es8388_init函数源码,在…...
洛谷 P1651 塔(DP)
题目传送门https://www.luogu.com.cn/problem/P1651 解题思路 设 表示前 个积木,两塔高度差为 (第一个比第二个高多少),的最大高度。 易得: 首先,不选当前的积木: 其次,选当前…...
去哪儿Java开发面试题及参考答案
怎么设置缓存能更快让用户收到数据? 要设置缓存以更快让用户收到数据,可从以下几方面着手。首先,选择合适的缓存位置很关键。将缓存放置在离用户近的地方,如 CDN 缓存,能极大缩短数据传输距离与时间。对于动态内容,可在应用服务器本地设置内存缓存,像使用 Ehcache 等库,…...
DDOS分布式拒绝服务攻击
DDOS分布式拒绝服务攻击 简单来说 传统的DOS就是一台或者多台服务对一个受害目标(服务器,路由,ip,国家)进行攻击,当范围过大时就是DDOS。目的就是通过大规模的网络流量使得正常流量不能访问受害目标&…...
AI后端工程师面试题的内容
AI后端工程师面试题主要包括以下几个方面的内容: 一、技术基础和项目经验: 1. 微服务架构的理解和应用:请描述你对微服务架构的理解,并举例说明一个你参与过的微服务项目,阐述你在该项目中扮演的角色和所承…...
使用go语言写一个脚本 实现WebSockt连接 用户发送a 得到返回b
在Go语言中实现一个简单的WebSocket服务器,该服务器能够接收客户端发送的“a”并返回“b”,可以按照以下步骤进行。首先,确保你的环境中已经安装了Go语言环境。接下来,你需要安装一个WebSocket库来处理WebSocket连接。这里我们使用…...
浏览器中输入一个URL后,按下回车后发生了什么
URL ,统一资源定位符, 简单点就是网址 ip 或域名 端口号 资源位置 参数 锚点 大致流程 URL 解析DNS 查询TCP 连接处理请求接受响应渲染页面 1 .输入一个网址之后,首先浏览器通过查询 DNS ,查找这个 URL 的 IP …...
ISO26262-(Timing Monitoring)在多核MCU的TPU上功能安全ASILB与ASILD有什么区别
在多核微控制器(MCU)的时间保护方面,针对功能安全ASIL B与ASILD等级的设计和实施存在显著差异,这些差异主要体现在系统对时间关键性操作的保障程度、故障检测能力、以及系统响应的严格性上。 ASIL B 级别: 时间关键性:在ASIL B等级,系统设计注重于识别并处理大部分可能…...
【大数据学习 | 面经】Spark 3.x 中的AQE(自适应查询执行)
Spark 3.x 中的自适应查询执行(Adaptive Query Execution,简称 AQE)通过多种方式提升性能,主要包括以下几个方面: 动态合并 Shuffle 分区(Coalescing Post Shuffle Partitions): 当 …...
mdcsoft服务器网络安全解决方案-SQL注入解决
mdcsoft服务器网络安全解决方案 最近几周,很多站被SQL注入攻击频繁,很多服务器被入侵,网站被攻击,为解决以上问题,我们开发了 以下系列软件及制定了系列解决方案.追究原因,全部都是认为引起的,由…...
防火墙之自定义链
自定义链的概念 自定义链(Custom Chain)通常是指在计算机网络中,根据特定需求创建的自定义规则链。这些规则链可以用于处理网络数据包,例如防火墙规则、路由规则等。自定义链的主要目的是为了提高网络管理的灵活性和可定制性。 …...
【Go底层】time包中Timer定时器原理
目录 1、背景2、go版本3、源码解释【1】Timer结构【2】NewTimer函数解释【3】After和AfterFunc函数解释 4、Timer定时间隔执行任务5、总结 1、背景 之前讲过Ticker定时器,每隔一段时间往通道写入当前时间。time包中还提供了另一种定时器:Timerÿ…...
Python毕业设计选题:基于Flask的医疗预约与诊断系统
开发语言:Python框架:flaskPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 系统首页 疾病信息 就诊信息 个人中心 管理员登录界面 管理员功能界面 用户界面 医生…...
Ajax基础总结(思维导图+二维表)
一些话 刚开始学习Ajax的时候,感觉很模糊,但是好像学什么都是这样的,很正常,但是当你学习的时候要持续性敲代码,边敲代码其实就可以理解很多了。然后在最后的总结,其实做二维表之后,就可以区分…...
Leetcode62. 不同路径(HOT100)
链接 我的代码: class Solution { public:int uniquePaths(int m, int n) {if(m<1||n<1)return 1;vector<vector<int>> dp(m,vector<int>(n));for(int i 0;i<m;i){for(int j 0;j<n;j){if(!i&&!j)dp[i][j] 0;else if(!i|…...
使用go实现一个简单的rpc
什么是rpc, rpc是干什么的?几种协议的压测数据对比:tcphttp 使用tcp实现一个简单的rpc服务 什么是rpc, rpc是干什么的? rpc的作用就是实现远程的服务调用 工作流程: 客户端携带服务信息(服务名,方法名)数据 去请求服务端,服务端拿到数据,解析后执行对应的方法,将结果返回给客…...
基于STM32的智能工业温度监测与控制系统设计
目录 引言系统设计 硬件设计软件设计系统功能模块 温度采集模块温控模块实时监控与报警模块数据记录与上传模块代码实现 4.1 温度采集模块4.2 温控模块4.3 实时监控与报警模块4.4 数据记录与上传模块系统调试与优化结论与展望 1. 引言 工业领域对温度的监控和控制至关重要&am…...
机器学习概述,特征工程简述2.1——2.3
机器学习概述: 1.1人工智能概述 达特茅斯会议—人工智能的起点 机器学习是人工智能的一个实现途径 深度学习是机器学习的一个方法发展而来 1.1.2 机器学习和深度学习能做什么 传统预测 图像识别 自然语言处理 1.2什么是机器学习 数据 模型 预测 从历史数…...
蓝桥杯准备训练(lesson1,c++方向)
前言 报名参加了蓝桥杯(c)方向的宝子们,今天我将与大家一起努力参赛,后序会与大家分享我的学习情况,我将从最基础的内容开始学习,带大家打好基础,在每节课后都会有练习题,刚开始的练…...
【导航查询】.NET开源 ORM 框架 SqlSugar 系列
.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...
k8s的数据库etcd报 etcdserver: mvcc: database space exceeded的处理办法
一.问题现象 公司的k8s集群的etcd配置是默认配置,其磁盘配置为2GB的配额,目前出现了数据写入失败的情况,报错Error: etcdserver: mvcc: database space exceeded。 二.处理思路 当etcd的磁盘使用达到2G后,可能会触发维护模式&am…...
【系统架构设计师】高分论文:论信息系统的安全与保密设计
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 摘要正文摘要 本人所在工作单位承担了我市城乡智慧建设工程综合管理平台项目的开发工作。我有幸参与了本项目,并担任架构师一职,全面负责项目的需求分析和系统设计等工作。城乡智慧建设工程综合管理平台项目包括…...
二分查找!
问题描述 小明在图书馆借阅书籍,图书馆的书籍在系统中按序号顺次排列,小明在借阅后,需在系统中从“在馆书籍列表”中将该书删除。请帮助小明编写一个函数,在现有列表{1, 3, 5, 6, 7, 10, 12, 14, 26, 32, 35, 39, 42, 45, 54, 56…...
学习方法的进一步迭代————4
今天又在怀疑第二大脑的可靠程度 为什么呢? 还是因为自己没记住东西,感觉没学到东西。 其实自己知道大脑本就不应该用来存放知识而是用来思考知识,但是自己还是陷在里面了,我觉得其本质不是因为认知还不够,也不是因为还有点不适…...
java将word docx pdf转换为图片(不需要额外下载压缩包,直接导入maven坐标)
(本代码实现的是将第1页转为图片,主要用于制作文件缩略图) pdf转图片容易 docx转图片麻烦,看其他博客可以直接导入maven坐标,但我知道那是需要付费且有时限的包 本着简单实用的心,我找到法子了 pdf转图片:有库直接转…...
Oracle 12c Data Guard 环境中的 GAP 修复方法
概述 上文中提到Oracle 12c 引入了多项新技术来简化 Data Guard 环境中的 GAP 修复过程,如(RECOVER … FROM SERVICE)。这些新特性不仅减少了操作步骤,还提高了效率和准确性。本文档将详细说明如何利用这些新特性进行 GAP 修复。…...
C语言:指针与数组
一、. 数组名的理解 int arr[5] { 0,1,2,3,4 }; int* p &arr[0]; 在之前我们知道要取一个数组的首元素地址就可以使用&arr[0],但其实数组名本身就是地址,而且是数组首元素的地址。在下图中我们就通过测试看出,结果确实如此。 可是…...
【Linux】理解文件系统
目录 理解磁盘物理结构存储结构 磁盘的逻辑结构逻辑抽象CHS && LBA地址的转化 文件系统块概念分区概念inode Ext2文件系统宏观认识Boot BlockBlock Group超级块(Super Block)块组描述符表(Group Descriptor Table)块位图&…...