排他网关定义
1. 排他网关exclusive gateway 经常使用流程变量决定流程下一步要选择的路径
2. 排他网关(也叫异或网关 XOR gateway,或者更专业的,基于数据的排他网关 exclusive data¬based gateway),用于为流程中的决策建模。
3. 当执行到达这个网关时,所有出口顺序流会按照它们定义的顺序进行计算。条件计算为true的顺序流(当没有设置条件时,认为顺序流定义为true)会被选择用于继续流程。
4. 用排他网关时,只会选择一条顺序流。当多条顺序 流的条件都计算为true时,其中在XML中定义的第一条(也只有这条)会被选择,用于继续流程。如果没有可选的顺序流,会抛出异常。
5. 排他网关,用内部带有’X’图标的标准网关(菱形)表示,'X’图标代表异或(XOR)的含义。请注意内部没有图标的网关默认为排他网关。BPMN 2.0规范不允许在同一个流程中,混合使用带有及没有X的菱形标志
6. 一个排他网关对应一个以上的顺序流
案例描述
首先我们模拟一下业务场景,就以请假流程为例。 如图所示根据流程变量days决定路径选择。其中HR是一条默认路径。接着我们看一下生成的BPMN文件的定义。
<bpmn:process id="Process_1ojbyyd" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_00pc376</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:exclusiveGateway id="Gateway_1rc1dcw">
<bpmn:incoming>Flow_00pc376</bpmn:incoming>
<bpmn:outgoing>Flow_02rt1d1</bpmn:outgoing>
<bpmn:outgoing>Flow_104rp3g</bpmn:outgoing>
<bpmn:outgoing>Flow_1ugfvox</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_00pc376" sourceRef="StartEvent_1" targetRef="Gateway_1rc1dcw" />
<!--days小于2天的路径-->
<bpmn:sequenceFlow id="Flow_02rt1d1" name="days<=2" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0sopid2">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days < 2</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<!--默认路径-->
<bpmn:sequenceFlow id="Flow_104rp3g" name="default" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0dw2l26" />
<bpmn:userTask id="Activity_0sopid2" name="直属领导">
<bpmn:incoming>Flow_02rt1d1</bpmn:incoming>
<bpmn:outgoing>Flow_1u8dcgu</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_0dw2l26" name="HR">
<bpmn:incoming>Flow_104rp3g</bpmn:incoming>
<bpmn:outgoing>Flow_1n7t0sw</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="Event_1aaq42i">
<bpmn:incoming>Flow_1u8dcgu</bpmn:incoming>
<bpmn:incoming>Flow_1n7t0sw</bpmn:incoming>
<bpmn:incoming>Flow_0mbnt14</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1u8dcgu" sourceRef="Activity_0sopid2" targetRef="Event_1aaq42i" />
<bpmn:sequenceFlow id="Flow_1n7t0sw" sourceRef="Activity_0dw2l26" targetRef="Event_1aaq42i" />
<!--days大于2天且小于5天的路径-->
<bpmn:sequenceFlow id="Flow_1ugfvox" name="2<days<=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days >2 and days <=5</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:userTask id="Activity_0ylxig7" name="部门经理">
<bpmn:incoming>Flow_1ugfvox</bpmn:incoming>
<bpmn:outgoing>Flow_0mbnt14</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0mbnt14" sourceRef="Activity_0ylxig7" targetRef="Event_1aaq42i" />
</bpmn:process>
上面是案例完整的BPMN文件定义。 具体到排他网关。 我们需要将顺序流相关定义信息单独抽出进行分析。经过整理。 如下就是排他网关定义相关的信息。
<!--days小于2天的路径-->
<bpmn:sequenceFlow id="Flow_02rt1d1" name="days<=2" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0sopid2">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days < 2</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<!--days大于2天且小于5天的路径-->
<bpmn:sequenceFlow id="Flow_1ugfvox" name="2<days<=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days >2 and days <=5</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<!--默认路径-->
<bpmn:sequenceFlow id="Flow_104rp3g" name="default" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0dw2l26" />
模拟排他网关的执行代码
根据上述定义, 我们首先要思考如何将xml文件转换成Java对象。首先就是这个顺序流的java映射。
<bpmn:sequenceFlow id="Flow_1ugfvox" name="2<days<=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days >2 and days <=5</bpmn:conditionExpression>
</bpmn:sequenceFlow>
根据这个定义文件。我们可以映射的定义一个Transition迁移类。
public class Transition{
//顺序流的标识
public String id;
//顺序流的名称
public String name;
//是否默认
public Boolean isDefault;
//开始节点
public String startRef;
//结束节点
public String targetRef;
//条件
private Condtion condition;
}
然后模拟一个排他网关的类。ExclusiveGateway.并完成如下伪代码
public class ExclusiveGateway{
//默认路径
private List<Transition> defaultList;
//非默认路径
private List<Transition> unDefaultList;
//路径计算方法
public State doLeave(){
//目标节点
State To = null;
//优先遍历非默认路径
for(Transition transtion : unDefaultList){
//处理迁移逻辑
}
//未找到默认路径。遍历默认路径集合
if(ObjectUtils.isNull(To)){
for(Transition transtion : defaultList){
//处理迁移逻辑
}
}
}
}