解讀Camunda中強(qiáng)大的監(jiān)聽(tīng)服務(wù)
簡(jiǎn)介
Camunda預(yù)覽了很多接口,以便于我們擴(kuò)展,其中最重要的莫過(guò)于各種監(jiān)聽(tīng)接口,本文就將接受三個(gè)最終常用的接口:
ExecutionListenerJavaDelegateTaskListener
并介紹下面3個(gè)ListenerType的區(qū)別:
Java ClassExpressionDelegate expression
創(chuàng)建工程
我們還是使用SpringBoot來(lái)創(chuàng)建工程,可以通過(guò)下面鏈接生成一個(gè)集成Camunda的SpringBoot工程
也可以直接拷貝下面的pom自己創(chuàng)建:
<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>
如果不想使用默認(rèn)的H2數(shù)據(jù)庫(kù),可以參考下面修改配置文件: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
兩點(diǎn)需要注意:
- 需要添加相關(guān)數(shù)據(jù)庫(kù)驅(qū)動(dòng)lib
- camunda.bpm.database.schema-update好像不生效,需要手動(dòng)創(chuàng)建一下Camunda相關(guān)表
自己測(cè)試,如果怕麻煩,直接使用默認(rèn)的H2數(shù)據(jù)庫(kù)即可。
然后就可以直接啟動(dòng)了:
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
JavaDelegate
對(duì)于Camunda的服務(wù)節(jié)點(diǎn),我們可以綁定一個(gè)Java類(lèi),這個(gè)Java類(lèi)只需要實(shí)現(xiàn)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
對(duì)于用戶節(jié)點(diǎn),我們可以配置TaskListener,來(lái)監(jiān)聽(tīng)用戶節(jié)點(diǎn)的一些事件,如:
createassignmentdeletecompleteupdatetimeout

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
基本所有節(jié)點(diǎn)都可以配置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());
// 獲取參數(shù)
delegateExecution.getVariable("paramA");
// 設(shè)置參數(shù)
delegateExecution.setVariable("paramZ","paramZValue");
// 可以獲取ProcessEngine、RuntimeService來(lái)執(zhí)行更多的操作
RuntimeService runtimeService = delegateExecution.getProcessEngine().getRuntimeService();
System.out.println(runtimeService);
System.out.println(CustomExecutionListener.class.getName() + "--end");
}
}
部署發(fā)起流程



CustomExecutionListener開(kāi)始節(jié)點(diǎn)
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用戶節(jié)點(diǎn)
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的優(yōu)勢(shì)在于不用繼承特別的類(lèi),就可以調(diào)用相關(guān)方法。
注意:需要將類(lèi)注入容器
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);
}
}

表達(dá)式中的execution是內(nèi)置變量,Camunda會(huì)自動(dòng)注入一個(gè)DelegateExecution實(shí)例,還有一個(gè)常用的內(nèi)置變量task,Camunda會(huì)自動(dòng)注入一個(gè)DelegateTask實(shí)例。
Delegate Expression
Delegate Expression可以看做是加強(qiáng)版的Expression。
它最大的一個(gè)特點(diǎn)是對(duì)于實(shí)現(xiàn)了:
ExecutionListenerJavaDelegateTaskListener
上面的類(lèi),Delegate Expression可以直接配置bean名稱就可以,Camunda會(huì)自動(dòng)調(diào)用其方法。
- 還是需要注入容器才行。
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>
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文詳解MySql外連接查詢?cè)赟pringBoot中的具體使用
外連接通常分為左外連接,右外連接和全外連接,這篇文章主要為大家詳細(xì)介紹了如何在SpringBoot中使用MySql的外連接查詢,需要的可以參考下2025-02-02
Java基礎(chǔ)篇之serialVersionUID用法及注意事項(xiàng)詳解
這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)篇之serialVersionUID用法及注意事項(xiàng)的相關(guān)資料,SerialVersionUID屬性是用于序列化/反序列化可序列化類(lèi)的對(duì)象的標(biāo)識(shí)符,我們可以用它來(lái)記住可序列化類(lèi)的版本,以驗(yàn)證加載的類(lèi)和序列化對(duì)象是否兼容,需要的朋友可以參考下2024-02-02
Spring事務(wù)中@Transactional注解不生效的原因分析與解決
在Spring框架中,@Transactional注解是管理數(shù)據(jù)庫(kù)事務(wù)的核心方式,本文將深入分析事務(wù)自調(diào)用的底層原理,解釋為什么事務(wù)不生效,并提供多種解決方案,希望對(duì)大家有所幫助2025-03-03
簡(jiǎn)單了解Java斷言利器AssertJ原理及用法
這篇文章主要介紹了簡(jiǎn)單了解Java斷言利器AssertJ原理及用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
在SpringBoot里自定義Spring MVC配置的三種方法
SpringBoot幫我們自動(dòng)配好了 MVC 的核心組件,但實(shí)際開(kāi)發(fā)中,光用默認(rèn)配置肯定不夠今天就教你 3 種自定義 Spring MVC 配置的方法,從簡(jiǎn)單到復(fù)雜,新手也能一步步跟著做,需要的朋友可以參考下2026-03-03
java時(shí)間戳與日期相互轉(zhuǎn)換工具詳解
這篇文章主要為大家詳細(xì)介紹了java各種時(shí)間戳與日期之間相互轉(zhuǎn)換的工具,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
mapstruct的用法之qualifiedByName示例詳解
qualifiedByName的意思就是使用這個(gè)Mapper接口中的指定的默認(rèn)方法去處理這個(gè)屬性的轉(zhuǎn)換,而不是簡(jiǎn)單的get?set,今天通過(guò)本文給大家介紹下mapstruct的用法之qualifiedByName示例詳解,感興趣的朋友一起看看吧2022-04-04
JAVA學(xué)習(xí)進(jìn)階篇之時(shí)間與日期相關(guān)類(lèi)
在日常的開(kāi)發(fā)工作當(dāng)中,我們經(jīng)常需要用到日期相關(guān)的類(lèi),下面這篇文章主要給大家介紹了關(guān)于JAVA學(xué)習(xí)進(jìn)階篇之時(shí)間與日期相關(guān)類(lèi)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09

