解读Camunda中强大的监听服务

 更新时间:2025年09月02日 09:34:05   作者:trayvontang  
本文介绍Camunda中三种常用监听接口(ExecutionListener、JavaDelegate、TaskListener)及其区别,强调Expression和DelegateExpression的使用优势,并指导Spring Boot工程搭建与数据库配置,建议使用H2数据库简化开发

简介

Camunda预览了很多接口,以便于我们扩展,其中最重要的莫过于各种监听接口,本文就将接受三个最终常用的接口:

  1. ExecutionListener
  2. JavaDelegate
  3. TaskListener

并介绍下面3个ListenerType的区别:

  1. Java Class
  2. Expression
  3. Delegate expression

创建工程

我们还是使用SpringBoot来创建工程,可以通过下面链接生成一个集成Camunda的SpringBoot工程

SpringBoot Camunda工程生成

也可以直接拷贝下面的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

两点需要注意:

  1. 需要添加相关数据库驱动lib
  2. 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,来监听用户节点的一些事件,如:

  1. create
  2. assignment
  3. delete
  4. complete
  5. update
  6. 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。

它最大的一个特点是对于实现了:

  1. ExecutionListener
  2. JavaDelegate
  3. 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>

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 一文详解MySql外连接查询在SpringBoot中的具体使用

    一文详解MySql外连接查询在SpringBoot中的具体使用

    外连接通常分为左外连接,右外连接和全外连接,这篇文章主要为大家详细介绍了如何在SpringBoot中使用MySql的外连接查询,需要的可以参考下
    2025-02-02
  • Java基础篇之serialVersionUID用法及注意事项详解

    Java基础篇之serialVersionUID用法及注意事项详解

    这篇文章主要给大家介绍了关于Java基础篇之serialVersionUID用法及注意事项的相关资料,SerialVersionUID属性是用于序列化/反序列化可序列化类的对象的标识符,我们可以用它来记住可序列化类的版本,以验证加载的类和序列化对象是否兼容,需要的朋友可以参考下
    2024-02-02
  • Spring事务中@Transactional注解不生效的原因分析与解决

    Spring事务中@Transactional注解不生效的原因分析与解决

    在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为什么事务不生效,并提供多种解决方案,希望对大家有所帮助
    2025-03-03
  • 简单了解Java断言利器AssertJ原理及用法

    简单了解Java断言利器AssertJ原理及用法

    这篇文章主要介绍了简单了解Java断言利器AssertJ原理及用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • 在SpringBoot里自定义Spring MVC配置的三种方法

    在SpringBoot里自定义Spring MVC配置的三种方法

    SpringBoot帮我们自动配好了 MVC 的核心组件,但实际开发中,光用默认配置肯定不够今天就教你 3 种自定义 Spring MVC 配置的方法,从简单到复杂,新手也能一步步跟着做,需要的朋友可以参考下
    2026-03-03
  • 浅谈javap命令拆解字节码文件

    浅谈javap命令拆解字节码文件

    这篇文章主要介绍了拆解字节码文件javap命令,对反编译感兴趣的同学可以参考下
    2021-04-04
  • java时间戳与日期相互转换工具详解

    java时间戳与日期相互转换工具详解

    这篇文章主要为大家详细介绍了java各种时间戳与日期之间相互转换的工具,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Mybatis如何直接执行SQL语句

    Mybatis如何直接执行SQL语句

    这篇文章主要介绍了Mybatis如何直接执行SQL语句,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • mapstruct的用法之qualifiedByName示例详解

    mapstruct的用法之qualifiedByName示例详解

    qualifiedByName的意思就是使用这个Mapper接口中的指定的默认方法去处理这个属性的转换,而不是简单的get set,今天通过本文给大家介绍下mapstruct的用法之qualifiedByName示例详解,感兴趣的朋友一起看看吧
    2022-04-04
  • JAVA学习进阶篇之时间与日期相关类

    JAVA学习进阶篇之时间与日期相关类

    在日常的开发工作当中,我们经常需要用到日期相关的类,下面这篇文章主要给大家介绍了关于JAVA学习进阶篇之时间与日期相关类的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09

最新评论