这是用户在 2024-4-2 11:35 为 https://docs.camunda.org/manual/7.16/user-guide/process-engine/delegation-code/#set-business-key-fro... 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

Delegation Code

Delegation Code allows you to execute external Java code, scripts or evaluate expressions when certain events occur during process execution.
委派代码允许您在进程执行期间发生某些事件时执行外部 Java 代码、脚本或计算表达式。

There are different types of Delegation Code:

  • Java Delegates can be attached to a BPMN Service Task.
    Java 委托可以附加到 BPMN 服务任务。
  • Delegate Variable Mapping can be attached to a Call Activity.
  • Execution Listeners can be attached to any event within the normal token flow, e.g., starting a process instance or entering an activity.
  • Task Listeners can be attached to events within the user task lifecycle, e.g., creation or completion of a user task.

You can create generic Delegation Code and configure this via the BPMN 2.0 XML using so called Field Injection.
您可以创建通用的委派代码,并使用所谓的字段注入通过 BPMN 2.0 XML 进行配置。

Java Delegate Java 委托

To implement a class that can be called during process execution, this class needs to implement the org.camunda.bpm.engine.delegate.JavaDelegate interface and provide the required logic in the execute method. When process execution arrives at this particular step, it will execute this logic defined in that method and leave the activity in the default BPMN 2.0 way.
若要实现可在进程执行期间调用的类,此类需要实现 org.camunda.bpm.engine.delegate.JavaDelegate 接口并在 execute 方法中提供所需的逻辑。当流程执行到达此特定步骤时,它将执行该方法中定义的此逻辑,并以默认的 BPMN 2.0 方式保留活动。

As an example let’s create a Java class that can be used to change a process variable String to uppercase. This class needs to implement the org.camunda.bpm.engine.delegate.JavaDelegate interface, which requires us to implement the execute(DelegateExecution) method. It’s this operation that will be called by the engine and which needs to contain the business logic. Process instance information such as process variables and other information can be accessed and manipulated through the DelegateExecution interface (click on the link for a detailed Javadoc of its operations).
例如,让我们创建一个 Java 类,该类可用于将进程变量 String 更改为大写。这个类需要实现接口 org.camunda.bpm.engine.delegate.JavaDelegate ,这就要求我们实现 execute(DelegateExecution) 方法。引擎将调用此操作,并且需要包含业务逻辑。可以通过 DelegateExecution 接口访问和操作流程实例信息,例如流程变量和其他信息(单击链接以获取其操作的详细 Javadoc)。

  public class ToUppercase implements JavaDelegate {

    public void execute(DelegateExecution execution) throws Exception {
      String var = (String) execution.getVariable("input");
      var = var.toUpperCase();
      execution.setVariable("input", var);


Note! 注意!

Each time a delegation class referencing activity is executed, a separate instance of this class will be created. This means that each time an activity is executed there will be used another instance of the class to call execute(DelegateExecution).
每次执行委派类引用活动时,都会创建此类的单独实例。这意味着每次执行活动时,都会使用该类的另一个实例来调用 execute(DelegateExecution)

The classes that are referenced in the process definition (i.e., by using camunda:class ) are NOT instantiated during deployment. Only when a process execution arrives at the point in the process where the class is used for the first time, an instance of that class will be created. If the class cannot be found, a ProcessEngineException will be thrown. The reason for this is that the environment (and more specifically the classpath) when you are deploying is often different than the actual runtime environment.
在流程定义中引用的类(即通过使用 camunda:class )在部署期间不会实例化。只有当进程执行到达进程中首次使用该类的点时,才会创建该类的实例。如果找不到该类,则将抛出 a ProcessEngineException 。这样做的原因是,部署时的环境(更具体地说是类路径)通常与实际的运行时环境不同。

Activity Behavior 活动行为

Instead of writing a Java Delegate, it is also possible to provide a class that implements the org.camunda.bpm.engine.impl.pvm.delegate.ActivityBehavior interface. Implementations then have access to the more powerful ActivityExecution that for example also allows to influence the control flow of the process. However, note that this is not a very good practice and should be avoided as much as possible. So, it is advised to only use the ActivityBehavior interface for advanced use cases and if you know exactly what you’re doing.
除了编写 Java 委托之外,还可以提供实现接口的 org.camunda.bpm.engine.impl.pvm.delegate.ActivityBehavior 类。然后,实现可以访问更强大的功能 ActivityExecution ,例如,还可以影响过程的控制流。但是,请注意,这不是一个很好的做法,应尽可能避免。因此,建议仅在高级用例中使用该 ActivityBehavior 界面,并且如果您确切地知道自己在做什么。

Field Injection 现场注入

It is possible to inject values into the fields of the delegated classes. The following types of injection are supported:

  • Fixed string values 固定字符串值
  • Expressions 表达 式

If available, the value is injected through a public setter method on your delegated class, following the Java Bean naming conventions (e.g., field firstName has setter setFirstName(...)). If no setter is available for that field, the value of private member will be set on the delegate (but using private fields is not recommended - see warning below).
如果可用,则按照 Java Bean 命名约定(例如,field firstName has setter)通过公共 setter setFirstName(...) 方法在委托类上注入该值。如果该字段没有可用的 setter,则将在委托上设置私有成员的值(但不建议使用私有字段 - 请参阅下面的警告)。

Regardless of the type of value declared in the process-definition, the type of the setter/private field on the injection target should always be org.camunda.bpm.engine.delegate.Expression.
无论在进程定义中声明的值类型如何,注入目标上的 setter/private 字段的类型都应始终为 org.camunda.bpm.engine.delegate.Expression

Private fields cannot always be modified! It does not work with e.g., CDI beans (because you have proxies instead of real objects) or with some SecurityManager configurations. Please always use a public setter-method for the fields you want to have injected!
私有字段不能总是被修改!例如,它不适用于 CDI Bean(因为您使用的是代理而不是真实对象)或某些 SecurityManager 配置。请始终对要注入的字段使用公共 setter-method!

The following code snippet shows how to inject a constant value into a field. Field Injection is supported when using the class or delegateExpression attribute. Note that we need to declare a extensionElements XML element before the actual field injection declarations, which is a requirement of the BPMN 2.0 XML Schema.
以下代码片段演示如何将常量值注入字段。使用 class or delegateExpression 属性时支持字段注入。请注意,我们需要在实际的字段注入声明之前声明一个 extensionElements XML 元素,这是 BPMN 2.0 XML 模式的要求。

  <serviceTask id="javaService"
               name="Java service invocation"
        <camunda:field name="text" stringValue="Hello World" />

The class ToUpperCaseFieldInjected has a field text which is of type org.camunda.bpm.engine.delegate.Expression. When calling text.getValue(execution), the configured string value Hello World will be returned.
该类 ToUpperCaseFieldInjected 的字段 text 类型为 org.camunda.bpm.engine.delegate.Expression 。调用 text.getValue(execution) 时,将返回配置的字符串值 Hello World

Alternatively, for longs texts (e.g., an inline e-mail) the camunda:string sub element can be used:
或者,对于长文本(例如,内联电子邮件),可以使用 camunda:string 子元素:

  <serviceTask id="javaService"
               name="Java service invocation"
      <camunda:field name="text">
            Hello World

To inject values that are dynamically resolved at runtime, expressions can be used. Those expressions can use process variables, CDI or Spring beans. As already noted, a separate instance of the Java class will be created each time the service task is executed. To have dynamic injection of values in fields, you can inject value and method expressions in an org.camunda.bpm.engine.delegate.Expression which can be evaluated/invoked using the DelegateExecution passed in the execute method.
若要注入在运行时动态解析的值,可以使用表达式。这些表达式可以使用进程变量、CDI 或 Spring Bean。如前所述,每次执行服务任务时,都会创建一个单独的 Java 类实例。要在字段中动态注入值,可以在可以使用 org.camunda.bpm.engine.delegate.Expression execute 方法 DelegateExecution 中传入的值和方法表达式进行计算/调用。

  <serviceTask id="javaService" name="Java service invocation"

      <camunda:field name="text1">
      <camunda:field name="text2">
         <camunda:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</camunda:expression>

The example class below uses the injected expressions and resolves them using the current DelegateExecution.
下面的示例类使用注入的表达式,并使用当前 DelegateExecution .

  public class ReverseStringsFieldInjected implements JavaDelegate {

    private Expression text1;
    private Expression text2;

    public void execute(DelegateExecution execution) {
      String value1 = (String) text1.getValue(execution);
      execution.setVariable("var1", new StringBuffer(value1).reverse().toString());

      String value2 = (String) text2.getValue(execution);
      execution.setVariable("var2", new StringBuffer(value2).reverse().toString());

Alternatively, you can also set the expressions as an attribute instead of a child-element, to make the XML less verbose.
或者,也可以将表达式设置为属性而不是子元素,以使 XML 不那么冗长。

  <camunda:field name="text1" expression="${genderBean.getGenderString(gender)}" />
  <camunda:field name="text2" expression="Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}" />

Note! 注意!

The injection happens each time the service task is called since a separate instance of the class will be created. When the fields are altered by your code, the values will be re-injected when the activity is executed next time.

For the same reasons as mentioned above, field injection should not be (usually) used with Spring beans, which are singletons by default. Otherwise, you may run into inconsistencies due to concurrent modification of the bean fields.

Delegate Variable Mapping

To implement a class that delegates the input and output variable mapping for a call activity, this class needs to implement the org.camunda.bpm.engine.delegate.DelegateVariableMapping interface. The implementation must provide the methods mapInputVariables(DelegateExecution, VariableMap) and mapOutputVariables(DelegateExecution, VariableScope). See the following example:

public class DelegatedVarMapping implements DelegateVariableMapping {

  public void mapInputVariables(DelegateExecution execution, VariableMap variables) {
    variables.putValue("inputVar", "inValue");

  public void mapOutputVariables(DelegateExecution execution, VariableScope subInstance) {
    execution.setVariable("outputVar", "outValue");

The mapInputVariables method is called before the call activity is executed, to map the input variables. The input variables should be put into the given variables map. The mapOutputVariables method is called after the call activity was executed, to map the output variables. The output variables can be directly set into the caller execution. The behavior of the class loading is similar to the class loading on Java Delegates.

Execution Listener

Execution listeners allow you to execute external Java code or evaluate an expression when certain events occur during process execution. The events that can be captured are:

  • Start and end of a process instance.
  • Taking a transition.
  • Start and end of an activity.
  • Start and end of a gateway.
  • Start and end of intermediate events.
  • Ending a start event or starting an end event.

The following process definition contains 3 execution listeners:

  <process id="executionListenersProcess">
          class="org.camunda.bpm.examples.bpmn.executionlistener.ExampleExecutionListenerOne" />

    <startEvent id="theStart" />

    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

    <userTask id="firstTask" />

    <sequenceFlow sourceRef="firstTask" targetRef="secondTask">
          <camunda:script scriptFormat="groovy">
            println execution.eventName

    <userTask id="secondTask">
        <camunda:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />

    <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" />

    <userTask id="thirdTask" />

    <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" />

    <endEvent id="theEnd" />

The first execution listener is notified when the process starts. The listener is an external Java-class (like ExampleExecutionListenerOne) and should implement the org.camunda.bpm.engine.delegate.ExecutionListener interface. When the event occurs (in this case end event) the method notify(DelegateExecution execution) is called.

  public class ExampleExecutionListenerOne implements ExecutionListener {

    public void notify(DelegateExecution execution) throws Exception {
      execution.setVariable("variableSetInExecutionListener", "firstValue");
      execution.setVariable("eventReceived", execution.getEventName());

It is also possible to use a delegation class that implements the org.camunda.bpm.engine.delegate.JavaDelegate interface. These delegation classes can then be reused in other constructs, such as a delegation for a service task.

The second execution listener is called when the transition is taken. Note that the listener element doesn’t define an event, since only take events are fired on transitions. Values in the event attribute are ignored when a listener is defined on a transition. Also it contains a camunda:script child element which defines a script which will be executed as execution listener. Alternatively it is possible to specify the script source code as external resources (see the documentation about script sources of script tasks).

The last execution listener is called when activity secondTask ends. Instead of using the class on the listener declaration, a expression is defined instead which is evaluated/invoked when the event is fired.

  <camunda:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />

As with other expressions, execution variables are resolved and can be used. Because the execution implementation object has a property that exposes the event name, it’s possible to pass the event-name to your methods using execution.eventName.

Execution listeners also support using a delegateExpression, similar to a service task.

  <camunda:executionListener event="start" delegateExpression="${myExecutionListenerBean}" />

Task Listener

A task listener is used to execute custom Java logic or an expression upon the occurrence of a certain task-related event. It can only be added in the process definition as a child element of a user task. Note that this also must happen as a child of the BPMN 2.0 extensionElements and in the Camunda namespace, since a task listener is a construct specifically for the Camunda engine.

  <userTask id="myTask" name="My Task" >
      <camunda:taskListener event="create" class="org.camunda.bpm.MyTaskCreateListener" />

Task Listener Event Lifecycle

The execution of Task Listeners is dependent on the order of firing of the following task-related events:

The create event fires when the task has been created and all task properties are set. No other task-related event will be fired before the create event. The event allows us to inspect all properties of the task when we receive it in the create listener.

The update event occurs when a task property (e.g. assignee, owner, priority, etc.) on an already created task is changed. This includes attributes of a task (e.g. assignee, owner, priority, etc.), as well as dependent entities (e.g. attachments, comments, task-local variables). Note that the initialization of a task does not fire an update event (the task is being created). This also means that the update event will always occur after a create event has already occurred.

The assignment event specifically tracks the changes of the Task’s assignee property. The event may be fired on two occasions:

  1. When a task with an assignee explicitly defined in the process definition has been created. In this case, the assignment event will be fired after the create event.
  2. When an already created task is assigned, i.e. the Task’s assignee property is changed. In this case, the assignment event will follow the update event since changing the assignee property results in an updated task.

The assignment event can be used for a more fine grained inspection, when the assignee is actually set.

The timeout event occurs when a Timer, associated with this Task Listener, is due. Note that this requires for a Timer to be defined. The timeout event may occur after a Task has been created, and before it has been completed.

The complete event occurs when the task is successfully completed and just before the task is deleted from the runtime data. A successful execution of a task’s complete Task Listeners results in an end of the task event lifecycle.

The delete event occurs just before the task is deleted from the runtime data, because of:

  1. An interrupting Boundary Event;
  2. An interrupting Event Subprocess;
  3. A Process Instance deletion;
  4. A BPMN Error thrown inside a Task Listener.

No other event is fired after the delete event since it results in an end of the task event lifecycle. This means that the delete event is mutually exclusive with the complete event.

Task Event Chaining

The descriptions above lay out the order in which Task Events are fired. However, this order may be disrupted under the following conditions:

  1. When calling Task#complete() inside a Task Listener, the complete event will be fired right away. The related Task Listeners will be immediately invoked, after which the remaining Task Listeners for the previous event will be processed.
  2. By using the TaskService methods inside a Task Listener, which may cause the firing of additional Task Events. As with the complete event mentioned above, these Task Events will immediately invoke their related Listeners, after which the remaining Task Listeners will be processed. However, it should be noted that the chain of events triggered inside the Task Listener, by the invocation of the TaskService method, will be in the previously described order.
  3. By throwing a BPMN Error event inside a Task Listener (e.g. a complete event Task Listener). This would cancel the Task and cause a delete event to be fired.

Under the above-mentioned conditions, users should be careful not to accidentally create a Task event loop.

Defining a Task Listener

A task listener supports the following attributes:

  • event (required): the type of task event on which the task listener will be invoked. Possible events are: create, assignment, update, complete, delete and timeout;

    Note that the timeout event requires a timerEventDefinition child element in the task listener and will only be fired if the Job Executor is enabled.

  • class: the delegation class that must be called. This class must implement the org.camunda.bpm.engine.impl.pvm.delegate.TaskListener interface.

    public class MyTaskCreateListener implements TaskListener {
      public void notify(DelegateTask delegateTask) {
        // Custom logic goes here

    It is also possible to use Field Injection to pass process variables or the execution to the delegation class. Note that each time a delegation class referencing activity is executed, a separate instance of this class will be created.

  • expression: (cannot be used together with the class attribute): specifies an expression that will be executed when the event happens. It is possible to pass the DelegateTask object and the name of the event (using task.eventName) to the called object as parameters.

    <camunda:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
  • delegateExpression: allows to specify an expression that resolves to an object implementing the TaskListener interface, similar to a service task.

    <camunda:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
  • id: a unique identifier of the listener within the scope of the user task, only required if the event is set to timeout.

Besides the class, expression and delegateExpression attributes, a camunda:script child element can be used to specify a script as task listener. An external script resource can also be declared with the resource attribute of the camunda:script element (see the documentation about script sources of script tasks).

  <userTask id="task">
      <camunda:taskListener event="create">
        <camunda:script scriptFormat="groovy">
          println task.eventName

Furthermore, a timerEventDefinition child element can be used in conjunction with the event type timeout in order to define the associated timer. The specified delegate will be called by the Job Executor when the timer is due. The execution of the user task will not be interrupted by this.

  <userTask id="task">
      <camunda:taskListener event="timeout" delegateExpression="${myTaskListenerBean}" id="friendly-reminder" >
          <timeDuration xsi:type="tFormalExpression">PT1H</timeDuration>

Field Injection on Listener

When using listeners configured with the class attribute, Field Injection can be applied. This is exactly the same mechanism as described for Java Delegates, which contains an overview of the possibilities provided by field injection.

The fragment below shows a simple example process with an execution listener with fields injected:

  <process id="executionListenersProcess">
      <camunda:executionListener class="org.camunda.bpm.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener" event="start">
        <camunda:field name="fixedValue" stringValue="Yes, I am " />
        <camunda:field name="dynamicValue" expression="${myVar}" />

    <startEvent id="theStart" />
    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

    <userTask id="firstTask" />
    <sequenceFlow sourceRef="firstTask" targetRef="theEnd" />

    <endEvent id="theEnd" />

The actual listener implementation may look as follows:

  public class ExampleFieldInjectedExecutionListener implements ExecutionListener {

    private Expression fixedValue;

    private Expression dynamicValue;

    public void notify(DelegateExecution execution) throws Exception {
      String value =
        fixedValue.getValue(execution).toString() +

      execution.setVariable("var", value);

The class ExampleFieldInjectedExecutionListener concatenates the 2 injected fields (one fixed and the other dynamic) and stores this in the process variable var.

  @Deployment(resources = {
  public void testExecutionListenerFieldInjection() {
    Map<String, Object> variables = new HashMap<String, Object>();
    variables.put("myVar", "listening!");

    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("executionListenersProcess", variables);

    Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var");
    assertTrue(varSetByListener instanceof String);

    // Result is a concatenation of fixed injected field and injected expression
    assertEquals("Yes, I am listening!", varSetByListener);

Access Process Engine Services

It is possible to access the public API services (RuntimeService, TaskService, RepositoryService …) from the Delegation Code. The following is an example showing how to access the TaskService from a JavaDelegate implementation.

  public class DelegateExample implements JavaDelegate {

    public void execute(DelegateExecution execution) throws Exception {
      TaskService taskService = execution.getProcessEngineServices().taskService();


Throw BPMN Errors from Delegation Code
从委派代码引发 BPMN 错误

It is possible to throw BpmnError from delegation code (Java Delegate, Execution and Task Listeners). This is done by using a provided Java exception class from within your Java code (e.g., in the JavaDelegate):
可以从委派代码(Java Delegate、Execution 和 Task Listeners)中抛出 BpmnError 。这是通过使用 Java 代码中提供的 Java 异常类(例如,在 JavaDelegate 中)来完成的:

public class BookOutGoodsDelegate implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    try {
    } catch (NotOnStockException ex) {
        throw new BpmnError(NOT_ON_STOCK_ERROR);


Throw BPMN Errors from Listeners
从侦听器抛出 BPMN 错误

When implementing an error catch event, keep in mind that the BpmnError will be caught when they are thrown in normal flow of the following listeners:
实现错误捕获事件时,请记住,当它们被抛入以下侦听器的正常流中时, BpmnError 将被捕获:

  • start and end execution listeners on activity, gateway, and intermediate events
  • take execution listeners on transitions
  • create, assign, and complete task listeners

The BpmnError will not be caught for the following listeners:
对于以下侦听器, BpmnError 不会被捕获:

  • start and end process listeners
  • delete task listeners 删除任务侦听器
  • listeners invoked outside of the normal flow:
    • a process modification is performed which trigger subprocess scope initialization and some of its listeners throws an error
    • a process instance deletion invokes an end listener throwing an error
    • a listener is triggered due to interrupting boundary event execution, e.g message correlation on subprocess invokes end listeners throwing an error

Note! 注意!

Throwing a BpmnError in the delegation code behaves like modelling an error end event. See the reference guide about the details on the behavior, especially the error boundary event. If no error boundary event is found on the scope, the execution is ended.
在委派代码中抛出 a BpmnError 的行为类似于对错误结束事件进行建模。请参阅参考指南,了解有关行为的详细信息,尤其是错误边界事件。如果在作用域上未找到错误边界事件,则执行结束。

Set Business Key from Delegation Code

The option to set a new value of business key to already running process instance is shown in the example below:

public class BookOutGoodsDelegate implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    String recalculatedKey = (String) execution.getVariable("recalculatedKeyVariable");


On this Page: 本页内容:

Want to get paid for developing Open Source Software?

Check out our open positions