解读Camunda中强大的监听服务
作者:trayvontang
本文介绍Camunda中三种常用监听接口(ExecutionListener、JavaDelegate、TaskListener)及其区别,强调Expression和DelegateExpression的使用优势,并指导Spring Boot工程搭建与数据库配置,建议使用H2数据库简化开发
简介
Camunda预览了很多接口,以便于我们扩展,其中最重要的莫过于各种监听接口,本文就将接受三个最终常用的接口:
ExecutionListener
JavaDelegate
TaskListener
并介绍下面3个ListenerType的区别:
Java Class
Expression
Delegate expression
创建工程
我们还是使用SpringBoot来创建工程,可以通过下面链接生成一个集成Camunda的SpringBoot工程
也可以直接拷贝下面的pom自己创建:
<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> <groupId>com.example.workflow</groupId> <artifactId>my-project</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.1.1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.camunda.bpm</groupId> <artifactId>camunda-bom</artifactId> <version>7.20.0</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId> </dependency> <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId> </dependency> <dependency> <groupId>org.camunda.bpm</groupId> <artifactId>camunda-engine-plugin-spin</artifactId> </dependency> <dependency> <groupId>org.camunda.spin</groupId> <artifactId>camunda-spin-dataformat-all</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>3.1.1</version> </plugin> </plugins> </build> </project>
如果不想使用默认的H2数据库,可以参考下面修改配置文件:application.yaml
#spring.datasource.url: jdbc:h2:file:./camunda-h2-database camunda.bpm.admin-user: id: demo password: demo #camunda.bpm.database: # schema-update: drop-create spring.datasource: url: jdbc:mysql://127.0.0.1:3306/clearn?characterEncoding=UTF-8&useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: tim password: pd
两点需要注意:
- 需要添加相关数据库驱动lib
- camunda.bpm.database.schema-update好像不生效,需要手动创建一下Camunda相关表
自己测试,如果怕麻烦,直接使用默认的H2数据库即可。
然后就可以直接启动了:
@SpringBootApplication public class Application { public static void main(String... args) { SpringApplication.run(Application.class, args); } }
JavaDelegate
对于Camunda的服务节点,我们可以绑定一个Java类,这个Java类只需要实现org.camunda.bpm.engine.delegate.JavaDelegate接口即可。
- Type选Java Class:
import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.JavaDelegate; public class CustomJavaDelegate implements JavaDelegate { @Override public void execute(DelegateExecution delegateExecution) throws Exception { System.out.println(CustomJavaDelegate.class.getName() + "--start"); System.out.println("getProcessInstanceId:" + delegateExecution.getProcessInstanceId()); System.out.println("getActivityInstanceId:" + delegateExecution.getActivityInstanceId()); System.out.println("getCurrentActivityId:" + delegateExecution.getCurrentActivityId()); System.out.println("getCurrentActivityName:" + delegateExecution.getCurrentActivityName()); System.out.println("getProcessBusinessKey:" + delegateExecution.getProcessBusinessKey()); System.out.println("getProcessDefinitionId:" + delegateExecution.getProcessDefinitionId()); System.out.println("getBusinessKey:" + delegateExecution.getBusinessKey()); System.out.println("getEventName:" + delegateExecution.getEventName()); Double paramA = (Double) delegateExecution.getVariable("paramA"); String paramB = (String) delegateExecution.getVariable("paramB"); System.out.println(paramA); System.out.println(paramB); delegateExecution.setVariable("CustomJavaDelegateP1","p1"); delegateExecution.setVariable("CustomJavaDelegateP2",500); System.out.println(CustomJavaDelegate.class.getName() + "--end"); } }
TaskListener
对于用户节点,我们可以配置TaskListener,来监听用户节点的一些事件,如:
create
assignment
delete
complete
update
timeout
import org.camunda.bpm.engine.delegate.DelegateTask; import org.camunda.bpm.engine.delegate.TaskListener; public class CustomTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { System.out.println(CustomTaskListener.class.getName() + "--start"); System.out.println("processInstanceId:" + delegateTask.getProcessInstanceId()); System.out.println("getAssignee:" + delegateTask.getAssignee()); System.out.println("getId:" + delegateTask.getId()); System.out.println("getName:" + delegateTask.getName()); System.out.println("getEventName:" + delegateTask.getEventName()); System.out.println("getPriority:" + delegateTask.getPriority()); System.out.println("getCaseDefinitionId:" + delegateTask.getCaseDefinitionId()); System.out.println("getCaseExecutionId:" + delegateTask.getCaseExecutionId()); System.out.println("getDueDate:" + delegateTask.getDueDate()); System.out.println("getTaskDefinitionKey:" + delegateTask.getTaskDefinitionKey()); System.out.println("getOwner:" + delegateTask.getOwner()); System.out.println("getDescription:" + delegateTask.getDescription()); System.out.println(CustomTaskListener.class.getName() + "--end"); } }
ExecutionListener
基本所有节点都可以配置Execution listener:
import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.ExecutionListener; public class CustomExecutionListener implements ExecutionListener { @Override public void notify(DelegateExecution delegateExecution) throws Exception { System.out.println(CustomExecutionListener.class.getName() + "--start"); System.out.println("getProcessInstanceId:" + delegateExecution.getProcessInstanceId()); System.out.println("getProcessDefinitionId:" + delegateExecution.getProcessDefinitionId()); System.out.println("getActivityInstanceId:" + delegateExecution.getActivityInstanceId()); System.out.println("getCurrentActivityId:" + delegateExecution.getCurrentActivityId()); System.out.println("getCurrentActivityName:" + delegateExecution.getCurrentActivityName()); System.out.println("getProcessBusinessKey:" + delegateExecution.getProcessBusinessKey()); System.out.println("getBusinessKey:" + delegateExecution.getBusinessKey()); // 获取参数 delegateExecution.getVariable("paramA"); // 设置参数 delegateExecution.setVariable("paramZ","paramZValue"); // 可以获取ProcessEngine、RuntimeService来执行更多的操作 RuntimeService runtimeService = delegateExecution.getProcessEngine().getRuntimeService(); System.out.println(runtimeService); System.out.println(CustomExecutionListener.class.getName() + "--end"); } }
部署发起流程
CustomExecutionListener开始节点
com.example.workflow.delegate.CustomExecutionListener--start getProcessInstanceId:646f4f07-bf4c-11ee-a70e-00ffe7687986 getProcessDefinitionId:Process_1dnzxeh:3:830657fb-bf4a-11ee-b1f6-00ffe7687986 getActivityInstanceId:null getCurrentActivityId:StartEvent_1 getCurrentActivityName:null getProcessBusinessKey:listener-30 getBusinessKey:listener-30 org.camunda.bpm.engine.impl.RuntimeServiceImpl@1d380352 com.example.workflow.delegate.CustomExecutionListener--end
CustomExecutionListener
com.example.workflow.delegate.CustomExecutionListener--start getProcessInstanceId:646f4f07-bf4c-11ee-a70e-00ffe7687986 getProcessDefinitionId:Process_1dnzxeh:3:830657fb-bf4a-11ee-b1f6-00ffe7687986 getActivityInstanceId:StartEvent_1:64747f30-bf4c-11ee-a70e-00ffe7687986 getCurrentActivityId:StartEvent_1 getCurrentActivityName:null getProcessBusinessKey:listener-30 getBusinessKey:listener-30 org.camunda.bpm.engine.impl.RuntimeServiceImpl@1d380352 com.example.workflow.delegate.CustomExecutionListener--end
CustomJavaDelegate
com.example.workflow.delegate.CustomJavaDelegate--start getProcessInstanceId:646f4f07-bf4c-11ee-a70e-00ffe7687986 getActivityInstanceId:Activity_1claer7:6475dec2-bf4c-11ee-a70e-00ffe7687986 getCurrentActivityId:Activity_1claer7 getCurrentActivityName:bzService getProcessBusinessKey:listener-30 getProcessDefinitionId:Process_1dnzxeh:3:830657fb-bf4a-11ee-b1f6-00ffe7687986 getBusinessKey:listener-30 getEventName:null 3.14 haha com.example.workflow.delegate.CustomJavaDelegate--end
CustomExecutionListener
com.example.workflow.delegate.CustomExecutionListener--start getProcessInstanceId:646f4f07-bf4c-11ee-a70e-00ffe7687986 getProcessDefinitionId:Process_1dnzxeh:3:830657fb-bf4a-11ee-b1f6-00ffe7687986 getActivityInstanceId:Activity_1xjraoy:6476c927-bf4c-11ee-a70e-00ffe7687986 getCurrentActivityId:Activity_1xjraoy getCurrentActivityName:userTask getProcessBusinessKey:listener-30 getBusinessKey:listener-30 org.camunda.bpm.engine.impl.RuntimeServiceImpl@1d380352 com.example.workflow.delegate.CustomExecutionListener--end
CustomTaskListener用户节点
com.example.workflow.delegate.CustomTaskListener--start processInstanceId:646f4f07-bf4c-11ee-a70e-00ffe7687986 getAssignee:null getId:64771749-bf4c-11ee-a70e-00ffe7687986 getName:userTask getEventName:create getPriority:50 getCaseDefinitionId:null getCaseExecutionId:null getDueDate:null getTaskDefinitionKey:Activity_1xjraoy getOwner:null getDescription:null com.example.workflow.delegate.CustomTaskListener--end
Expression
Expression的优势在于不用继承特别的类,就可以调用相关方法。
注意:需要将类注入容器
import org.camunda.bpm.engine.delegate.DelegateExecution; import org.springframework.stereotype.Component; /** * #{expressionDelegateExecution.exe(execution)} */ @Component("expressionDelegateExecution") public class ExpressionDelegateExecution { public void exe(DelegateExecution execution){ String processInstanceId = execution.getProcessInstanceId(); System.out.println("ExpressionDelegateExecution:" + processInstanceId); } }
表达式中的execution是内置变量,Camunda会自动注入一个DelegateExecution实例,还有一个常用的内置变量task,Camunda会自动注入一个DelegateTask实例。
Delegate Expression
Delegate Expression可以看做是加强版的Expression。
它最大的一个特点是对于实现了:
ExecutionListener
JavaDelegate
TaskListener
上面的类,Delegate Expression可以直接配置bean名称就可以,Camunda会自动调用其方法。
- 还是需要注入容器才行。
import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.JavaDelegate; import org.springframework.stereotype.Component; /** * #{delegateExpressionComponent} */ @Component("delegateExpressionComponent") public class DelegateExpressionComponent implements JavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { System.out.println("DelegateExpressionComponent:" + execution.getProcessInstanceId()); } }
流程图
<?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_13pugtj" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.19.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0"> <bpmn:process id="Process_1dnzxeh" name="service" isExecutable="true" camunda:historyTimeToLive="180"> <bpmn:extensionElements> <camunda:executionListener class="com.example.workflow.delegate.CustomExecutionListener" event="end" /> <camunda:executionListener class="com.example.workflow.delegate.CustomExecutionListener" event="start" /> </bpmn:extensionElements> <bpmn:startEvent id="StartEvent_1"> <bpmn:extensionElements> <camunda:executionListener class="com.example.workflow.delegate.CustomExecutionListener" event="start" /> </bpmn:extensionElements> <bpmn:outgoing>Flow_0eji7wy</bpmn:outgoing> </bpmn:startEvent> <bpmn:serviceTask id="Activity_1claer7" name="bzService" camunda:class="com.example.workflow.delegate.CustomJavaDelegate"> <bpmn:incoming>Flow_0eji7wy</bpmn:incoming> <bpmn:outgoing>Flow_0c190eu</bpmn:outgoing> </bpmn:serviceTask> <bpmn:sequenceFlow id="Flow_0eji7wy" sourceRef="StartEvent_1" targetRef="Activity_1claer7" /> <bpmn:sequenceFlow id="Flow_0c190eu" sourceRef="Activity_1claer7" targetRef="Activity_1xjraoy" /> <bpmn:userTask id="Activity_1xjraoy" name="userTask"> <bpmn:extensionElements> <camunda:taskListener class="com.example.workflow.delegate.CustomTaskListener" event="create" id="Lid255" /> <camunda:executionListener class="com.example.workflow.delegate.CustomExecutionListener" event="start" /> </bpmn:extensionElements> <bpmn:incoming>Flow_0c190eu</bpmn:incoming> <bpmn:outgoing>Flow_0383lfn</bpmn:outgoing> </bpmn:userTask> <bpmn:endEvent id="Event_0aws3kb"> <bpmn:extensionElements> <camunda:executionListener class="com.example.workflow.delegate.CustomExecutionListener" event="start" /> </bpmn:extensionElements> <bpmn:incoming>Flow_0383lfn</bpmn:incoming> </bpmn:endEvent> <bpmn:sequenceFlow id="Flow_0383lfn" sourceRef="Activity_1xjraoy" targetRef="Event_0aws3kb" /> </bpmn:process> <bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1dnzxeh"> <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> <dc:Bounds x="179" y="99" width="36" height="36" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Activity_04xt5m2_di" bpmnElement="Activity_1claer7"> <dc:Bounds x="280" y="77" width="100" height="80" /> <bpmndi:BPMNLabel /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Activity_1frv2ve_di" bpmnElement="Activity_1xjraoy"> <dc:Bounds x="450" y="77" width="100" height="80" /> <bpmndi:BPMNLabel /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Event_0aws3kb_di" bpmnElement="Event_0aws3kb"> <dc:Bounds x="602" y="99" width="36" height="36" /> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="Flow_0eji7wy_di" bpmnElement="Flow_0eji7wy"> <di:waypoint x="215" y="117" /> <di:waypoint x="280" y="117" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_0c190eu_di" bpmnElement="Flow_0c190eu"> <di:waypoint x="380" y="117" /> <di:waypoint x="450" y="117" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_0383lfn_di" bpmnElement="Flow_0383lfn"> <di:waypoint x="550" y="117" /> <di:waypoint x="602" y="117" /> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </bpmn:definitions>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。