- 信号事件引用了一个命名的信号,信号是一个全局作用域(广播)事件,它将会被传递给所有的活动的信号handler。
下面就是一个两个不同的流程被信号关联起来的例子。第一个流程当接收到保险政策(insurance policy)被更新或者改变的信息时发起流程,在通过一个评审后,会抛出一个信号事件,通知所有监听这个信号的接收事件,保险政策已经改变了:

这个信号事件将会被感兴趣的流程实例捕获,下面这个例子就是一个订阅了这个信号事件的流程:

注意:信号是广播到所有的活动的handler,在上面的例子里,意味着所有捕获了这个信号的流程实例都会接收到这个信号事件。
一、定义一个信号事件(Signal Event Definition):
信号事件用signalEventDefinition元素进行声明。signalRef属性引用一个信号元素,这个信号元素被声明成definitions元素的子元素的。下面是一个流程摘要,一个信号事件被抛出并被中间事件捕获的流程,signalEventDefinitions引用的是全局的信号元素:
<definitions>
<!-- declaration of the signal -->
<signal id="alertSignal" name="alert" />
<process id="catchSignal">
<intermediateThrowEvent id="throwSignalEvent" name="Alert">
<!-- signal event definition -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateThrowEvent>
<!-- ... -->
<intermediateCatchEvent id="catchSignalEvent" name="On Alert">
<!-- signal event definition -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateCatchEvent>
<!-- ... -->
</process>
</definitions>
注意:与其他事件不一样,信号事件不会被消费掉(不是一次性的),如果你有两个活动的信号边界事件捕获到相同的信号事件,这个两个边界事件都会被触发,尽管他们是流程流程实例不同的部分。
表达式(Expressions):
信号事件的名称支持表达式,这个名称将会在流程到达信号作用域时被解析。例如当流程实例到达信号中间捕获事件时,这个表达式的名称将被解析。通过使用表达式,你可以通过流程变量动态的影响信号的名称。这在中断并行分支上会非常有用,下面是一个例子:
<signal id="alertSignal" name="alert-${execution.processBusinessKey}" />
信号的API(Signal Api):
一、触发(抛出)一个信号(Triggering (Throwing) Signals): 信号可以通过BPMN构造,或通过Java API编程构造抛出,RuntimeService提供了流式API去抛出一个信号:
// 广播一个信号
runtimeService
.createSignalEvent("signalName")
.setVariables(variables)
.send();
// 传递一个信号到单个execution
runtimeService
.createSignalEvent("signalName")
.executionId(executionId)
.setVariables(variables)
.send();
二、此外,您可以使用RuntimeService提供的以下方法之一:
RuntimeService.signalEventReceived(String signalName);
RuntimeService.signalEventReceived(String signalName, String executionId);
如果提供了executionId,则信号只会被传递给指定的execution。否则信号将会被全局抛出给所有订阅了的handler(广播)
注意:信号事件不会关联到特定的流程实例,相反,它会广播到所有流程实例。如果你需要传递给特定的流程实例,不要使用抛出信号事件(throwing signal event),查询出这个特定的执行(execution),然后将信号传递给这个特定的执行(execution)。
三、查询信号事件订阅(Querying for Signal Event Subscriptions): 可以查询所有订阅了某个特定信号事件的订阅
List<Execution> executions = runtimeService.createExecutionQuery()
.signalEventSubscriptionName("alert")
.list();
然后你可以用信号的API将信号传递给特定的执行
二、捕获信号事件(Catching Signal Events):
信号开始事件(Signal Start Event):
信号开始事件可以使用一个被命名的信号来发起流程实例
当部署的流程定义有一个或者多个信号开始事件,适用以下注意事项: 1.给定的流程定义中,信号开始事件的名称必须是唯一的,即流程定义不能有重复名称的信号开始事件,如果一个流程定义中的信号开始事件引用了相同的信号,或者有两个或者多个信号开始事件引用了相同的信号名称,流程会抛出一个异常。 2.与消息开始事件不同,信号开始事件的名称不要求所有部署的流程定义是唯一的 3.流程版本,在新部署的流程定义上,之前版本的信号订阅会被取消,以及新版本中没有提供的信号事件订阅也会被取消
当一个有正确名称的信号事件被抛出,流程定义中有一个或者多个信号开始事件的流程定义将会启动一个流程实例,当这个信号开始事件接收到有适当名称的信号时。这个信号可以被流程实例抛出(中间抛出信号事件或者信号结束事件时)或者使用RuntimeService的下列方法抛出:
void signalEventReceived(String signalName);
void signalEventReceived(String signalName, Map<String, Object> processVariables);
信号中间捕获事件(Signal Intermediate Catching Event):
当一个令牌到的信号中间捕获事件时,它将会等待在这里,直到一个带有合适名称的信号到达
<intermediateCatchEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateCatchEvent>
信号边界事件(Signal Boundary Event):
当执行到达信号边界事件附着的活动时,这个信号边界事件将会捕获带有合适名称的信号
注意:与其他事件想反,例如错误边界事件,信号边界事件不仅仅会捕获它的依附的作用域,信号事件是全局作用域的,意味着信号可以从任何地方抛出,甚至从其他流程实例抛出。
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<signalEventDefinition signalRef="alertSignal"/>
</boundaryEvent>
三、抛出信号事件(Throwing Signal Events):
信号中间抛出事件(Signal Intermediate Throwing Event):
信号中间抛出事件抛出一个定义了信号的信号事件 这个信号是广播到所有的handler(所有捕获信号事件),信号可以异步或者同步被发布
1.默认情况下,信号是同步传递的,这意味着抛出信号事件的流程会等到信号传递给所有捕获信号事件的流程实例。捕获信号事件的流程实例也会在同一个事物里通知抛出信号事件的流程实例,这意味着,一个接收信号事件的流程实例抛出一个技术错误,所有的相关的流程实例都会失败。 2.信号也可以异步传递,在这种情况下,这取决于捕获信号事件的活动在当时是不是活动的,当信号事件到达时。对于每一个信号捕获handler,异步通知消息(Job)是通过(JobExecutor)进行存储和传递的。
信号中间事件被定义成一个中间抛出事件,在这种情形下,这个特定类型的子元素是signalEventDefinition:
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateThrowEvent>
异步信号事件:
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" camunda:asyncBefore="true" />
</intermediateThrowEvent>
信号结束事件(Signal End Event):
信号结束事件抛出一个定义好的信号,并且当前执行路径结束。信号结束事件的行为与信号中间抛出事件类似。
<endEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</endEvent>
变量传递(Passing Variables):
流程变量(process variables)可以从抛出信号事件流程实例传递给捕获信号事件的流程实例。当数据以信号启动事件开始时,或者在信号中间捕获事件中离开等待状态之前,将数据复制到信号捕获过程实例中。
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in source="throwingVariableName" target="catchingVariableName" />
</extensionElements>
</signalEventDefinition>
在信号捕获流程实例中,将在“ camunda:in”元素中声明的变量设置为尽可能大的流程变量作用域。 在将数据传递到信号捕获流程实例之前,也可以使用表达式并对其进行修改。
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in sourceExpression="${X + 5}" target="Y" />
</extensionElements>
</signalEventDefinition>
这个在信号捕捉流程实例中的Y流程变量将会有(X+ 5)这个值,X流程变量是在流程抛出流程实例中。 此外,它还可以被声明成,将所有的信号抛出流程实例的变量传递给信号捕捉流程实例
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in variables="all" local="true" />
</extensionElements>
</signalEventDefinition>
通过设置local=“true”,可以仅仅传递抛出信号事件的流程的本地变量给捕获信号事件的流程。这些变量是被申明在作为输入参数(input parameters)
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in variables="all" local="true" />
</extensionElements>
</signalEventDefinition>
上提到的选项可以同时使用,例如下面的例子:
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in variables="all" local="true" />
<camunda:in source="X" target="Y" />
<camunda:in sourceExpression="${X + 5}" target="Z" />
</extensionElements>
</signalEventDefinition>
传递业务key(Passing a Business Key):
除了可以传递流程变量给信号捕获流程实例,也可以传递一个Business Key,然后这个Business Key只支持通过信号发起事件发起的流程。
<signalEventDefinition signalRef="newCustomerSignal">
<extensionElements>
<camunda:in businessKey="${execution.processBusinessKey}" />
</extensionElements>
</signalEventDefinition>
business key的camunda:in元素可以与流程变量传递camunda:in元素混合使用。