02 流程流转

审批流程图

如下图,在此流程图中,存在两个UserTask节点,第一个节点是主管审批,第二个节点是产品经理审批,两个节点中间有一个排他网关,此网关用来对主管审批的结果进行判断,如果主管审批通过,则流程走到产品经理审批节点,如果主管审批拒绝,则流程走到结束节点。

主管审批节点通过UEL表达式{assignManager}动态赋值,产品经理审批节点通过UEL表达式{assignProductLineManager}动态赋值,网关节点通过UEL表达式${isPass}动态赋值。

流程流转

上文 《01 启动流程实例》 讲到在完成流程实例的启动后,流程将通过commandContext.getAgenda().planContinueProcessOperation(execution)流转到下一个节点。本文将以此代码为入口,研究流程如何流转到下一个节点。

上面的代码中,getAgent 方法返回来一个 ActivitiEngineAgenda 接口,此接口的实现类是DefaultActivitiEngineAgenda,它的内部成员属性如下,operations 在这里充当 FIFO 队列,用来存储将要执行的 AbstractOperation (本质上是一个 Runnable 接口)操作,而 CommandContext 则是命令上下文。

java 复制代码
public class DefaultActivitiEngineAgenda implements ActivitiEngineAgenda {

    private static final Logger logger = LoggerFactory.getLogger(DefaultActivitiEngineAgenda.class);

    protected LinkedList<Runnable> operations = new LinkedList<Runnable>();
    protected CommandContext commandContext;
    
    public DefaultActivitiEngineAgenda(CommandContext commandContext) {
      this.commandContext = commandContext;
    }
    // 省略部分代码
}

ContinueProcessOperation 操作(第一次)

构造 ContinueProcessOperation

planContinueProcessOperation(execution) 在 DefaultActivitiEngineAgenda 中的实现如下,把传入的 execution 和 commandContext 构造一个 ContinueProcessOperation 实例,并放入了 operations 队列中,

java 复制代码
@Override
public void planContinueProcessOperation(ExecutionEntity execution) {
    planOperation(new ContinueProcessOperation(commandContext, execution));
}

@Override
public void planOperation(Runnable operation) {
    // 将 operation 任务放到 operations 队列中,等待定时任务从队列中取出任务并执行
    operations.add(operation);

    if (operation instanceof AbstractOperation) {
        ExecutionEntity execution = ((AbstractOperation) operation).getExecution();
        if (execution != null) {
            // 这里添加的内容,在 org/activiti/engine/impl/interceptor/CommandInvoker的execute(CommandConfig, Command<T>) 
            // 中有使用,目的是告诉 CommandInvoker 有待执行的 AbstractOperation
            commandContext.addInvolvedExecution(execution);
        }
    }

    logger.debug("Operation {} added to agenda", operation.getClass());
}

ContinueProcessOperation 类定义如下:

java 复制代码
public abstract class AbstractOperation implements Runnable {
    // 省略部分代码
}


public class ContinueProcessOperation extends AbstractOperation {
    // 省略部分代码
}

执行 ContinueProcessOperation

由异步任务执行器 org/activiti/engine/impl/asyncexecutor/DefaultAsyncJobExecutor 中的 AcquireAsyncJobsDueRunnable 调用 DefaultActivitiEngineAgenda 的 getNextOperation 来取出 operations 队列中的 AbstractOperation 任务,然后由 CommandInvoker 来执行(异步任务执行器 DefaultAsyncJobExecutor 是在我们调用 ProcessEngines.getDefaultProcessEngine() 创建 ProcessEngine 阶段完成的初始化)。

CommandInvoker 中执行 AbstractOperation 任务的代码是下面这一段

java 复制代码
protected void executeOperations(final CommandContext commandContext) {
  // 任务不是空的
  while (!commandContext.getAgenda().isEmpty()) {
    // 取出任务
    Runnable runnable = commandContext.getAgenda().getNextOperation();
    // 执行任务
    executeOperation(runnable);
  }
}

public void executeOperation(Runnable runnable) {
  // ContinueProcessOperation 继承 AbstractOperation,所以这个if条件的值是true,进入此if内部逻辑
  // 除了 ContinueProcessOperation 外, 其它继承 AbstractOperation 的Operation类都能进入这个if逻辑
  if (runnable instanceof AbstractOperation) {
    AbstractOperation operation = (AbstractOperation) runnable;

    // Execute the operation if the operation has no execution (i.e. it's an operation not working on a process instance)
    // or the operation has an execution and it is not ended
    if (operation.getExecution() == null || !operation.getExecution().isEnded()) {

      if (logger.isDebugEnabled()) {
        logger.debug("Executing operation {} ", operation.getClass());
      }
      // 直接调用 ContinueProcessOperation(Runnable)的run方法,
      // 会不会开启异步线程?不会,直接调用run方法,就是调用一个普通的方法
      runnable.run();

    }

  } else {
    // 直接调用 Runnable 的run方法,会不会开启异步线程?不会,直接调用run方法,就是调用一个普通的方法
    runnable.run();
  }
}

执行 ContinueProcessOperation 的 run() 方法后,来到了 ContinueProcessOperation 中

java 复制代码
public class ContinueProcessOperation extends AbstractOperation {

    // 省略部分代码
    /**
     * 当 ContinueProcessOperation 操作被执行时,以 run 方法作为入口
     */
    @Override
    public void run() {
        // 第一次执行这个逻辑,取出来的 currentFlowElement 是一个 StartEvent,StartEvent 是 FlowNode 的子类
        FlowElement currentFlowElement = getCurrentFlowElement(execution);
        if (currentFlowElement instanceof FlowNode) {
            // 继续执行 FlowNode 类型的类,StartEvent会走这个方法
            continueThroughFlowNode((FlowNode) currentFlowElement);
        } else if (currentFlowElement instanceof SequenceFlow) {
            // 继续执行 SequenceFlow 类型的类
            continueThroughSequenceFlow((SequenceFlow) currentFlowElement);
        } else {
            throw new ActivitiException("Programmatic error: no current flow element found or invalid type: " + currentFlowElement + ". Halting.");
        }
    }

    protected void continueThroughFlowNode(FlowNode flowNode) {

        // Check if it's the initial flow element. If so, we must fire the execution listeners for the process too
        if (flowNode.getIncomingFlows() != null
                && flowNode.getIncomingFlows().size() == 0
                && flowNode.getSubProcess() == null) {
            executeProcessStartExecutionListeners();
        }

        // For a subprocess, a new child execution is created that will visit the steps of the subprocess
        // The original execution that arrived here will wait until the subprocess is finished
        // and will then be used to continue the process instance.
        if (flowNode instanceof SubProcess) {
            createChildExecutionForSubProcess((SubProcess) flowNode);
        }

        if (flowNode instanceof Activity && ((Activity) flowNode).hasMultiInstanceLoopCharacteristics()) {
            // the multi instance execution will look at async
            executeMultiInstanceSynchronous(flowNode);
        } else if (forceSynchronousOperation || !flowNode.isAsynchronous()) {
            // 同步执行,StartEvent走的是这里
            executeSynchronous(flowNode);
        } else {
            // 异步执行
            executeAsynchronous(flowNode);
        }
    }

    /**
     * 同步执行
     * @param flowNode
     */
    protected void executeSynchronous(FlowNode flowNode) {
        // 调用HistoryManager记录流程活动开始了,这里并不是单单指StartEvent活动,而是所有活动
        commandContext.getHistoryManager().recordActivityStart(execution);

        // Execution listener: event 'start'
        // 执行 StartEvent 上的监听器,发布 Start 的 Event 事件
        if (CollectionUtil.isNotEmpty(flowNode.getExecutionListeners())) {
            executeExecutionListeners(flowNode, ExecutionListener.EVENTNAME_START);
        }

        // Execute any boundary events, sub process boundary events will be executed from the activity behavior
        if (!inCompensation && flowNode instanceof Activity) { // Only activities can have boundary events
            // 获取节点上的边界事件,如果有,就执行
            List<BoundaryEvent> boundaryEvents = ((Activity) flowNode).getBoundaryEvents();
            if (CollectionUtil.isNotEmpty(boundaryEvents)) {
                executeBoundaryEvents(boundaryEvents, execution);
            }
        }

        // Execute actual behavior
        // 取出节点上的行为
        ActivityBehavior activityBehavior = (ActivityBehavior) flowNode.getBehavior();
        // 当 activityBehavior 不为空,走此方法,此方法后续也会调用 planTakeOutgoingSequenceFlowsOperation
        // activityBehavior 表示一个节点上拥有的行为
        // StartEvent 节点的行为是 NoneStartEventActivityBehavior,没有做其它业务,仅仅是过度
        // UserTask   节点的行为是 UserTaskActivityBehavior,这个行为会把任务写入到数据库后,等待用户完成任务,流程才会继续走下去
        if (activityBehavior != null) {
            // 执行节点上的行为,StartEvent后走的是这里,
            executeActivityBehavior(activityBehavior, flowNode);
        } else {
            logger.debug("No activityBehavior on activity '{}' with execution {}", flowNode.getId(), execution.getId());
            // StartEvent后不走这里,计划执行 TakeOutgoingSequenceFlows 操作,这个操作是一个连线行为,第一步先找出当前节点的出口,第二步从出口走到下一个节点。
            Context.getAgenda().planTakeOutgoingSequenceFlowsOperation(execution, true);
        }
    }

    /**
     * 执行节点上的行为
     * @param activityBehavior
     * @param flowNode
     */
    protected void executeActivityBehavior(ActivityBehavior activityBehavior,
                                           FlowNode flowNode) {
        logger.debug("Executing activityBehavior {} on activity '{}' with execution {}",
                     activityBehavior.getClass(),
                     flowNode.getId(),
                     execution.getId());

        if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
            Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
                    ActivitiEventBuilder.createActivityEvent(ActivitiEventType.ACTIVITY_STARTED,
                                                             flowNode.getId(),
                                                             flowNode.getName(),
                                                             execution.getId(),
                                                             execution.getProcessInstanceId(),
                                                             execution.getProcessDefinitionId(),
                                                             flowNode));
        }

        try {
            // 这方法里面后续会执行 planTakeOutgoingSequenceFlowsOperation
            // 执行 StartEvent 的 NoneStartEventActivityBehavior 行为
            activityBehavior.execute(execution);
        } catch (RuntimeException e) {
            if (LogMDC.isMDCEnabled()) {
                LogMDC.putMDCExecution(execution);
            }
            throw e;
        }
    }
}

执行 NoneStartEventActivityBehavior

因为 NoneStartEventActivityBehavior 是空实现,但它继承自 FlowNodeActivityBehavior ,拥有了父类的能力,所以 activityBehavior.execute(execution) 实际执行的是父类FlowNodeActivityBehavior 的 execute(DelegateExecution) 方法。

java 复制代码
public class NoneStartEventActivityBehavior extends FlowNodeActivityBehavior {

  private static final long serialVersionUID = 1L;

  // Nothing to see here.
  // The default behaviour of the BpmnActivity is exactly what
  // a none start event should be doing.

}

在 execute(DelegateExecution) 方法中调用了 leave(execution) 方法,详情见代码

java 复制代码
public abstract class FlowNodeActivityBehavior implements TriggerableActivityBehavior {

  private static final long serialVersionUID = 1L;

  protected BpmnActivityBehavior bpmnActivityBehavior = new BpmnActivityBehavior();

  /**
   * Default behaviour: just leave the activity with no extra functionality.
   * NoneStartEventActivityBehavior 自己没有实现 execute 方法,所以会调用父类
   * FlowNodeActivityBehavior 的 execute 方法
   */
  public void execute(DelegateExecution execution) {
    leave(execution);
  }

  /**
   * Default way of leaving a BPMN 2.0 activity: evaluate the conditions on the outgoing sequence flow and take those that evaluate to true.
   * 离开 BPMN 2.0 activity的默认方式:评估 Outgoing sequence 流上的条件,并选取那些评估为 true 的条件
   */
  public void leave(DelegateExecution execution) {
    // 走到 bpmnActivityBehavior 中的 performDefaultOutgoingBehavior 方法
    bpmnActivityBehavior.performDefaultOutgoingBehavior((ExecutionEntity) execution);
  }
}

BpmnActivityBehavior 的 performDefaultOutgoingBehavior 内部调用 Context.getAgenda().planTakeOutgoingSequenceFlowsOperation(ExecutionEntity, boolean) 方法,这里的 Context.getAgenda() 获取到的 Agenda 和上面的 Agenda 是同一个,取到 Agenda后,调用了它的 planTakeOutgoingSequenceFlowsOperation(ExecutionEntity, boolean) 方法,此方法将产生连线行为 ,将 StartEvent 根据 Process 流程模型的定义连接到下一个节点。

java 复制代码
public class BpmnActivityBehavior implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * Performs the default outgoing BPMN 2.0 behavior, which is having parallel paths of executions for the outgoing sequence flow.
     * <p>
     * More precisely: every sequence flow that has a condition which evaluates to true (or which doesn't have a condition), is selected for continuation of the process instance. If multiple sequencer
     * flow are selected, multiple, parallel paths of executions are created.
     */
    public void performDefaultOutgoingBehavior(ExecutionEntity activityExecution) {
        performOutgoingBehavior(activityExecution,
                                true,
                                false);
    }
    
    /**
     * Actual implementation of leaving an activity.
     * @param execution The current execution context
     * @param checkConditions Whether or not to check conditions before determining whether or not to take a transition.
     * @param throwExceptionIfExecutionStuck If true, an {@link ActivitiException} will be thrown in case no transition could be found to leave the activity.
     * 最后在这个方法里调用了 planTakeOutgoingSequenceFlowsOperation 方法,用于走出当前就节点,走到下一个节点
     */
    protected void performOutgoingBehavior(ExecutionEntity execution,
                                           boolean checkConditions,
                                           boolean throwExceptionIfExecutionStuck) {
        Context.getAgenda().planTakeOutgoingSequenceFlowsOperation(execution,
                                                                   true);
    }
}

TakeOutgoingSequenceFlowsOperation 操作

构造 TakeOutgoingSequenceFlowsOperation

DefaultActivitiEngineAgenda 的 planTakeOutgoingSequenceFlowsOperation 方法如下,将 ExecutionEntity 封装成 TakeOutgoingSequenceFlowsOperation,再放入 operations 队列中。

java 复制代码
public class DefaultActivitiEngineAgenda implements ActivitiEngineAgenda {
    @Override
    public void planTakeOutgoingSequenceFlowsOperation(ExecutionEntity execution, boolean evaluateConditions) {
        planOperation(new TakeOutgoingSequenceFlowsOperation(commandContext, execution, evaluateConditions));
    }
}

执行 TakeOutgoingSequenceFlowsOperation

TakeOutgoingSequenceFlowsOperation 也和 ContinueProcessOperation 一样是 AbstractionOperation 的子类,同样也是由定时任务通过 CommandInvoker 来调用 TakeOutgoingSequenceFlowsOperation 中的 run() 方法。

java 复制代码
public class TakeOutgoingSequenceFlowsOperation extends AbstractOperation {
    
    // 省略部分代码

    @Override
    public void run() {
        FlowElement currentFlowElement = getCurrentFlowElement(execution);
        // Compensation check
        if ((currentFlowElement instanceof Activity)
                && (((Activity) currentFlowElement)).isForCompensation()) {
            cleanupCompensation();
            return;
        }

        // When leaving the current activity, we need to delete any related execution (eg active boundary events)
        // 清除当前节点的一些附属信息
        cleanupExecutions(currentFlowElement);
        // 当前 currentFlowElement 是一个 StartEvent,StartEvent 是 FlowNode 的子类
        if (currentFlowElement instanceof FlowNode) {
            // 处理 FlowNode 流节点
            handleFlowNode((FlowNode) currentFlowElement);
        } else if (currentFlowElement instanceof SequenceFlow) {
            // 处理 SequenceFlow 系列流
            handleSequenceFlow();
        }
    }
    
    /**
     * uai 处理流节点
     * @param flowNode
     */
    protected void handleFlowNode(FlowNode flowNode) {
        // 记录 StartEvent 活动结束
        handleActivityEnd(flowNode);
        if (flowNode.getParentContainer() != null
                && flowNode.getParentContainer() instanceof AdhocSubProcess) {
            handleAdhocSubProcess(flowNode);
        } else {
            // StartEvent走这里, 离开流节点
            leaveFlowNode(flowNode);
        }
    }
    
    /**
     * 离开流节点
     * @param flowNode
     */
    protected void leaveFlowNode(FlowNode flowNode) {
        
        // 省略部分代码
        String defaultSequenceFlowId = null;
        if (flowNode instanceof Activity) {
            defaultSequenceFlowId = ((Activity) flowNode).getDefaultFlow();
        } else if (flowNode instanceof Gateway) {
            defaultSequenceFlowId = ((Gateway) flowNode).getDefaultFlow();
        }
        // Determine which sequence flows can be used for leaving
        // 声明 List<SequenceFlow> 实例,用来存储符合外出流条件的 SequenceFlow
        List<SequenceFlow> outgoingSequenceFlows = new ArrayList<SequenceFlow>();
        // 获取 StartEvent 节点可以外出的顺序流,并放到外出顺序流集合 outgoingSequenceFlows 中
        for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
            String skipExpressionString = sequenceFlow.getSkipExpression();
            // 通过 isSkipExpressionEnabled 判断 execution 是否开启了跳过表达式
            if (!SkipExpressionUtil.isSkipExpressionEnabled(execution, skipExpressionString)) {
    
                if (!evaluateConditions || (evaluateConditions && ConditionUtil.hasTrueCondition(sequenceFlow, execution)
                                        && (defaultSequenceFlowId == null || !defaultSequenceFlowId.equals(sequenceFlow.getId())))) {
                    // 符合外出流条件的放入到 outgoingSequenceFlows
                    outgoingSequenceFlows.add(sequenceFlow);
                }
            } else if (flowNode.getOutgoingFlows().size() == 1 || SkipExpressionUtil.shouldSkipFlowElement(commandContext,
                                                                                                           execution,
                                                                                                           skipExpressionString)) {
                // The 'skip' for a sequence flow means that we skip the condition, not the sequence flow.
                // 符合外出流条件的放入到 outgoingSequenceFlows
                outgoingSequenceFlows.add(sequenceFlow);
            }
        }
    
        // Check if there is a default sequence flow
        if (outgoingSequenceFlows.size() == 0 && evaluateConditions) { // The elements that set this to false also have no support for default sequence flow
            if (defaultSequenceFlowId != null) {
                for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                    if (defaultSequenceFlowId.equals(sequenceFlow.getId())) {
                        // 符合外出流条件的放入到 outgoingSequenceFlows
                        outgoingSequenceFlows.add(sequenceFlow);
                        break;
                    }
                }
            }
        }

        // No outgoing found. Ending the execution
        // 如果没有当前节点没有可以外出的顺序流,则流程到当前节点后,设置为结束。StartEvent不走这里
        if (outgoingSequenceFlows.size() == 0) {
            if (flowNode.getOutgoingFlows() == null || flowNode.getOutgoingFlows().size() == 0) {
                logger.debug("No outgoing sequence flow found for flow node '{}'.", flowNode.getId());
                // 当前节点没有可以外出的顺序流,走向结束节点
                Context.getAgenda().planEndExecutionOperation(execution);
            } else {
                throw new ActivitiException("No outgoing sequence flow of element '" + flowNode.getId() + "' could be selected for continuing the process");
            }
        } else {
    
            // Leave, and reuse the incoming sequence flow, make executions for all the others (if applicable)
            ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
            List<ExecutionEntity> outgoingExecutions = new ArrayList<ExecutionEntity>(flowNode.getOutgoingFlows().size());
            // 当前节点有多个可以外出的出口顺序流,取出第一个
            SequenceFlow sequenceFlow = outgoingSequenceFlows.get(0);
    
            // Reuse existing one
            /**
             * uai
             * 给已经存在的execution(ExecutionEntity)实例更新状态,表示流程走到了 sequenceFlow 这个节点.
             * Process流程模型可以理解为一条高速公路,execution(ExecutionEntity)理解为一辆车,高速公路上有多个服务区,每个服务区是一个 sequenceFlow。
             * 现有一辆货车,我们规定它每走到一个服务区时车辆要加油或者司机要休息一下,在加油或者休息时司机需要把当前所在的服务区(sequenceFlow)告诉他的老板,让
             * 老板知道货运到哪里了(也就是在整个审批流程中,setCurrentFlowElement这一操作,让我们知道流程走到哪里了)
             */
            // 将 execution 中的 CurrentFlowElement 从之前的 StartEvent 更新为 SequenceFlow
            execution.setCurrentFlowElement(sequenceFlow);
            execution.setActive(true);
            // 将从 StartEvent 中出发的 execution 实例放入到 outgoingExecutions 链表中
            outgoingExecutions.add((ExecutionEntity) execution);
    
            // Executions for all the other one
            // 有多条出口,我的审批流程图中,StartEvent 只有一条外出流,所以不进入这个 if 语句
            if (outgoingSequenceFlows.size() > 1) {
                for (int i = 1; i < outgoingSequenceFlows.size(); i++) {
                    // 如果当前 execution 有 parent,则大家共用这个 parent,
                    // 如果当前 execution 没有 parent,则用当前 execution 作为 parent,
                    ExecutionEntity parent = execution.getParentId() != null ? execution.getParent() : execution;
                    // 根据 父ExecutionEntity 信息,创建 子ExecutionEntity 实例,相当于高速公里有分叉路
                    ExecutionEntity outgoingExecutionEntity = commandContext.getExecutionEntityManager().createChildExecution(parent);
    
                    SequenceFlow outgoingSequenceFlow = outgoingSequenceFlows.get(i);
                    // 给 子ExecutionEntity 实例设置当前节点为 outgoingSequenceFlow
                    outgoingExecutionEntity.setCurrentFlowElement(outgoingSequenceFlow);
                    // 存储到数据库
                    executionEntityManager.insert(outgoingExecutionEntity);
                    outgoingExecutions.add(outgoingExecutionEntity);
                }
            }
    
            // Leave (only done when all executions have been made, since some queries depend on this)
            // 把 outgoingExecution 放入到 Agenda 的队列中,计划离开当前节点,前往下一个节点
            // 这里可能有多个节点,所以通过遍历走所有的外出流,当前的审批流程图中,只有一条外出流,所以 outgoingExecutions 的大小是1
            for (ExecutionEntity outgoingExecution : outgoingExecutions) {
                // 准备执行 ContinueProcessOperation 操作
                Context.getAgenda().planContinueProcessOperation(outgoingExecution);
            }
        }
    }
}

上面这个代码,最重要的是从 StartEvent 节点的信息中找到了外出流 (SequenceFlow),随即调用 execution 的 setCurrentFlowElement 把活跃节点从之前的 StartEvent 更新为 SequenceFlow,这个操作表示流程从 StartEvent 走到了 SequenceFlow 这里。后续将从 SequenceFlow 继续走到下个节点。

分析上面代码发现流程最终又走到了 Context.getAgenda().planContinueProcessOperation(ExecutionEntity) 方法去执行 ContinueProcessOperation 中的业务,是不是重复执行了?

表面上看是重复执行了,但实际上有一些微妙的变化,这个变化是:传给 planContinueProcessOperation 方法的 execution 实例,它的 currentFlowElement 从 StartEvent 变成了 SequenceFlow。这个变化就会使得流程进入到 ContinueProcessOperation 的 else if 语句中,而不是第一次刚进来的 if 语句。

java 复制代码
public class ContinueProcessOperation extends AbstractOperation {

    // 省略部分代码
    /**
     * 当 ContinueProcessOperation 操作被执行时,以 run 方法作为入口
     */
    @Override
    public void run() {
        // 第一次执行这个逻辑,取出来的 currentFlowElement 是一个 StartEvent,StartEvent 是 FlowNode 的子类
        FlowElement currentFlowElement = getCurrentFlowElement(execution);
        if (currentFlowElement instanceof FlowNode) {
            // 继续执行 FlowNode 类型的类,StartEvent会走这个方法
            continueThroughFlowNode((FlowNode) currentFlowElement);
        } else if (currentFlowElement instanceof SequenceFlow) {
            // 继续执行 SequenceFlow 类型的类
            continueThroughSequenceFlow((SequenceFlow) currentFlowElement);
        } else {
            throw new ActivitiException("Programmatic error: no current flow element found or invalid type: " + currentFlowElement + ". Halting.");
        }
    }
}

ContinueProcessOperation 操作(第二次)

构造 ContinueProcessOperation

忽略阐述此重复内容。

执行 ContinueProcessOperation

接上面的内容,流程进入到 ContinueProcessOperation 的 else if 语句中,而不是第一次刚进来的 if 语句。

else if 语句中调用的方法是 continueThroughSequenceFlow(SequenceFlow)。

java 复制代码
/**
 * 执行 SequenceFlow 逻辑
 * @param sequenceFlow
 */
protected void continueThroughSequenceFlow(SequenceFlow sequenceFlow) {

    // Execution listener. Sequenceflow only 'take' makes sense ... but we've supported all three since the beginning
    // 执行 SequenceFlow 上的监听器
    if (CollectionUtil.isNotEmpty(sequenceFlow.getExecutionListeners())) {
        executeExecutionListeners(sequenceFlow,
                                  ExecutionListener.EVENTNAME_START);
        executeExecutionListeners(sequenceFlow,
                                  ExecutionListener.EVENTNAME_TAKE);
        executeExecutionListeners(sequenceFlow,
                                  ExecutionListener.EVENTNAME_END);
    }

    // Firing event that transition is being taken
    if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
        FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
        FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
        Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
                ActivitiEventBuilder.createSequenceFlowTakenEvent(
                        (ExecutionEntity) execution,
                        ActivitiEventType.SEQUENCEFLOW_TAKEN,
                        sequenceFlow.getId(),
                        sourceFlowElement != null ? sourceFlowElement.getId() : null,
                        sourceFlowElement != null ? (String) sourceFlowElement.getName() : null,
                        sourceFlowElement != null ? sourceFlowElement.getClass().getName() : null,
                        sourceFlowElement != null ? ((FlowNode) sourceFlowElement).getBehavior() : null,
                        targetFlowElement != null ? targetFlowElement.getId() : null,
                        targetFlowElement != null ? targetFlowElement.getName() : null,
                        targetFlowElement != null ? targetFlowElement.getClass().getName() : null,
                        targetFlowElement != null ? ((FlowNode) targetFlowElement).getBehavior() : null));
    }

    // 获取 sequenceFlow 的下一个节点
    FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
    // 更新 execution 当前所处的节点,前面更新过两次,第一次设置为 StartEvent,第二次设置为 SequenceFlow,当前第三次更新为审批流程图中的UserTask
    execution.setCurrentFlowElement(targetFlowElement);

    logger.debug("Sequence flow '{}' encountered. Continuing process by following it using execution {}",
                 sequenceFlow.getId(),
                 execution.getId());
    // 继续前往下个节点,在这里又执行了一次 planContinueProcessOperation
    Context.getAgenda().planContinueProcessOperation(execution);
}

上面代码中,最后一行又是 Context.getAgenda().planContinueProcessOperation(ExecutionEntity) 方法,此方法去执行 ContinueProcessOperation 中的业务。这一次传给 planContinueProcessOperation 方法的 execution 实例,它的 currentFlowElement 从 SequenceFlow 变成了 UserTask。

ContinueProcessOperation 操作(第三次)

构造 ContinueProcessOperation

忽略阐述此重复内容。

执行 ContinueProcessOperation

UserTask 和 StartEvent 一样,它也是 FlowNode 的子类,因此在 ContinueProcessOperation 的 run 方法中,流程进入到 if 语句 中。

java 复制代码
public class ContinueProcessOperation extends AbstractOperation {

    // 省略部分代码
    /**
     * 当 ContinueProcessOperation 操作被执行时,以 run 方法作为入口
     */
    @Override
    public void run() {
        // 第一次执行这个逻辑,取出来的 currentFlowElement 是一个 StartEvent,StartEvent 是 FlowNode 的子类
        FlowElement currentFlowElement = getCurrentFlowElement(execution);
        if (currentFlowElement instanceof FlowNode) {
            // 继续执行 FlowNode 类型的类,StartEvent会走这个方法
            continueThroughFlowNode((FlowNode) currentFlowElement);
        } else if (currentFlowElement instanceof SequenceFlow) {
            // 继续执行 SequenceFlow 类型的类
            continueThroughSequenceFlow((SequenceFlow) currentFlowElement);
        } else {
            throw new ActivitiException("Programmatic error: no current flow element found or invalid type: " + currentFlowElement + ". Halting.");
        }
    }
}

进入后,大致逻辑和 StartEvent 相似,但不同的点在于 UserTask 上的节点行为是 UserTaskActivityBehavior,此行为不像 StartEvent 的 NoneStartEventActivityBehavior 行为,UserTaskActivityBehavior 它定义了这个 UserTask 上的具体行为。在经过这个行为后,流程会暂时停在 UserTask 节点上,等待用户审批完成才会继续走下去。UserTask 节点的具体的行为见下一篇文章03 节点行为。下面这个代码和前面是重复的,为方便观察,所以粘贴一份。

java 复制代码
/**
 * 同步执行
 * @param flowNode
 */
protected void executeSynchronous(FlowNode flowNode) {
    // 调用HistoryManager记录流程活动开始了,这里并不是单单指StartEvent活动,而是所有活动
    commandContext.getHistoryManager().recordActivityStart(execution);

    // Execution listener: event 'start'
    // 执行 StartEvent 上的监听器,发布 Start 的 Event 事件
    if (CollectionUtil.isNotEmpty(flowNode.getExecutionListeners())) {
        executeExecutionListeners(flowNode, ExecutionListener.EVENTNAME_START);
    }

    // Execute any boundary events, sub process boundary events will be executed from the activity behavior
    if (!inCompensation && flowNode instanceof Activity) { // Only activities can have boundary events
        // 获取节点上的边界事件
        List<BoundaryEvent> boundaryEvents = ((Activity) flowNode).getBoundaryEvents();
        if (CollectionUtil.isNotEmpty(boundaryEvents)) {
            executeBoundaryEvents(boundaryEvents, execution);
        }
    }

    // Execute actual behavior
    // 执行实际的行为,UserTask对应的behavior是UserTaskActivityBehavior,这个UserTaskActivityBehavior里面会把task写入到act_ru_task
    ActivityBehavior activityBehavior = (ActivityBehavior) flowNode.getBehavior();
    // 当 activityBehavior 不为空,走此方法,此方法后续也会调用 planTakeOutgoingSequenceFlowsOperation
    // activityBehavior 表示一个节点上拥有的行为
    // StartEvent 节点的行为是 NoneStartEventActivityBehavior,没有做其它业务,仅仅是过度
    // UserTask   节点的行为是 UserTaskActivityBehavior,这个行为会把任务写入到数据库后,等待用户完成任务,流程才会继续走下去
    if (activityBehavior != null) {
        executeActivityBehavior(activityBehavior, flowNode);
    } else {
        logger.debug("No activityBehavior on activity '{}' with execution {}", flowNode.getId(), execution.getId());
        // 计划执行 TakeOutgoingSequenceFlows 操作,这个操作是一个连线行为,第一步先找出当前节点的出口,第二步从出口走到下一个节点。
        Context.getAgenda().planTakeOutgoingSequenceFlowsOperation(execution, true);
    }
}

总结

本章节内容有点绕也有点复杂,下面画个简图辅助理解。图中的 ExecutionEntity 是 ProcessInstance 接口的实现类,所以 ExecutionEntity 本身就是 ProcessInstance 的一个实例,这个实例叫 流程实例。一个流程实例至少有一个或多个 ExecutionEntity 实例,流程实例本身称之为 Parent ExecutionEntity,而流程实例下的一个或多个 ExecutionEntity 称之为 Sub ExecutionEntity, 它们之间属于父子关系。像本章开头的审批流程图,因没有并行分支,所以只会产生一个 Sub ExecutionEntity。


总结流程流转要点:

  1. 启动流程实例:创建 ExecutionEntity 的实例,并将此实例的 CurrentFlowElement 指向 StartEvent。
  2. 流程流转到SequenceFlow:ExecutionEntity 流转到 SequenceFlow,此时将 CurrentFlowElement 指向 SequenceFlow。
  3. 流程流转到UserTask:ExecutionEntity 流转到 UserTask,此时将 CurrentFlowElement 指向 UserTask。
  4. 以此类推,当 ExecutionEntity 流转到 EndEvent 时,将 CurrentFlowElement 指向 EndEvent,标记此流程实例结束。
    流程流转对应的示意图如下:
相关推荐
Warren9818 分钟前
Java Record 类 — 简化不可变对象的写法
java·开发语言·jvm·分布式·算法·mybatis·dubbo
SimonKing22 分钟前
流式数据服务端怎么传给前端,前端怎么接收?
java·后端·程序员
Laplaces Demon24 分钟前
Spring 源码学习(十)—— DispatcherServlet
java·后端·学习·spring
哈基米喜欢哈哈哈32 分钟前
进程和线程
java·linux·windows·笔记
咕白m62534 分钟前
Java 高效实现 Word 转 PDF - 掌握关键转换选项
java
都叫我大帅哥1 小时前
谁说数据库不能“直播”?用Debezium玩转实时数据流!
java
写bug写bug1 小时前
彻底搞懂Spring Boot的系统监控机制
java·后端·spring
墨城之左2 小时前
低版本 IntelliJ IDEA 使用高版本 JDK 语言特性的问题
java·开发语言·intellij-idea·jdk21