activiti学习(二十)——流程虚拟机源码分析(二)——从开始节点离开到下个节点前

本文主要是介绍activiti学习(二十)——流程虚拟机源码分析(二)——从开始节点离开到下个节点前,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简述

《activiti学习(十九)——流程虚拟机源码分析(一)——流程启动源码分析》一文中最后,流程从startEvent节点执行takeAll准备离开。本文我们分析一下从开始节点到下一个节点之前虚拟机做了什么。由于流程虚拟机条件分支情况很多,一一叙述非常繁琐(有些我自己理解也不透彻),所以我会用最简单的模型去跟踪其流转,如下图:

我们假设起点startEvent的下一个节点是一个普通的userTask,本文跟踪其在流程虚拟机的代码走向。

 

跟踪源码

上一篇文章我们跟踪到ExecutionEntity的takeAll方法。startEvent活动类通过调用这个方法离开startEvent节点。

public class ExecutionEntity extends VariableScopeImpl implements ActivityExecution, ExecutionListenerExecution, Execution, PvmExecution, ProcessInstance, InterpretableExecution, PersistentObject, HasRevision {//......public void takeAll(List<PvmTransition> transitions, List<ActivityExecution> recyclableExecutions) {fireActivityCompletedEvent();transitions = new ArrayList<PvmTransition>(transitions);recyclableExecutions = (recyclableExecutions!=null ? new ArrayList<ActivityExecution>(recyclableExecutions) : new ArrayList<ActivityExecution>());if (recyclableExecutions.size()>1) {for (ActivityExecution recyclableExecution: recyclableExecutions) {if (((ExecutionEntity)recyclableExecution).isScope()) {throw new PvmException("joining scope executions is not allowed");}}}ExecutionEntity concurrentRoot = ((isConcurrent && !isScope) ? getParent() : this);List<ExecutionEntity> concurrentActiveExecutions = new ArrayList<ExecutionEntity>();List<ExecutionEntity> concurrentInActiveExecutions = new ArrayList<ExecutionEntity>();for (ExecutionEntity execution: concurrentRoot.getExecutions()) {if (execution.isActive()) {concurrentActiveExecutions.add(execution);} else {concurrentInActiveExecutions.add(execution);}}if ( (transitions.size()==1)&& (concurrentActiveExecutions.isEmpty())&& allExecutionsInSameActivity(concurrentInActiveExecutions)) {List<ExecutionEntity> recyclableExecutionImpls = (List) recyclableExecutions;recyclableExecutions.remove(concurrentRoot);for (ExecutionEntity prunedExecution: recyclableExecutionImpls) {Context.getCommandContext().getHistoryManager().recordActivityEnd(prunedExecution);prunedExecution.remove();}concurrentRoot.setActive(true);concurrentRoot.setActivity(activity);concurrentRoot.setConcurrent(false);concurrentRoot.take(transitions.get(0), false);} else {
//......}public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {if (fireActivityCompletionEvent) {fireActivityCompletedEvent();}if (this.transition!=null) {throw new PvmException("already taking a transition");}if (transition==null) {throw new PvmException("transition is null");}setActivity((ActivityImpl)transition.getSource());setTransition((TransitionImpl) transition);performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);}//......
}

第8行fireActivityCompletedEvent方法会触发全局事件转发器转发ACTIVITY_COMPLETED事件。11行由于入参recyclableExecutions为空,所以为recyclableExecutions新建一个空的数组。22行isConcurrent默认为false,isScope默认是true,因此concurrentRoot赋值为this。32-34行判断假设startEvent的“出线”只有一条,则此时判断为true。48行通过take离开startEvent节点。66行设置当前活动为startEvent的ActivitiImpl,67行设置当前的连线,68行调用原子操作performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END)。这个原子操作的调用我们上一篇文章分析过,这里不再细述。看看AtomicOperation.TRANSITION_NOTIFY_LISTENER_END对应的AtomicOperationTransitionNotifyListenerEnd类:

public class AtomicOperationTransitionNotifyListenerEnd extends AbstractEventAtomicOperation {//......protected String getEventName() {return org.activiti.engine.impl.pvm.PvmEvent.EVENTNAME_END;}protected void eventNotificationsCompleted(InterpretableExecution execution) {execution.performOperation(TRANSITION_DESTROY_SCOPE);}
}

这里原子操作首先会触发实行监听器的end事件,然后执行原子操作performOperation(TRANSITION_DESTROY_SCOPE)。即调用AtomicOperationTransitionDestroyScope类

public class AtomicOperationTransitionDestroyScope implements AtomicOperation {//......@SuppressWarnings("unchecked")public void execute(InterpretableExecution execution) {InterpretableExecution propagatingExecution = null;ActivityImpl activity = (ActivityImpl) execution.getActivity();if (activity.isScope()) {      
//......} else {propagatingExecution = execution;}ScopeImpl nextOuterScopeElement = activity.getParent();TransitionImpl transition = propagatingExecution.getTransition();ActivityImpl destination = transition.getDestination();if (transitionLeavesNextOuterScope(nextOuterScopeElement, destination)) {propagatingExecution.setActivity((ActivityImpl) nextOuterScopeElement);propagatingExecution.performOperation(TRANSITION_NOTIFY_LISTENER_END);} else {propagatingExecution.performOperation(TRANSITION_NOTIFY_LISTENER_TAKE);}}public boolean transitionLeavesNextOuterScope(ScopeImpl nextScopeElement, ActivityImpl destination) {return !nextScopeElement.contains(destination);}
}

执行execution方法,第10行的if判断为false,注意execution的isScope为true,但是ActivityImpl的isScope为false,所以这里执行13行的代码。16行获取activity.getParent(),即startEvent的parent,是Process对象。18行获取连线终点的活动。19行判断Process中是否含有下一个节点的活动,因此28行函数判断为false(!符号取了相反的值),因此走23行performOperation(TRANSITION_NOTIFY_LISTENER_TAKE),会调用AtomicOperationTransitionNotifyListenerTake类。

public class AtomicOperationTransitionNotifyListenerTake implements AtomicOperation {//......public void execute(InterpretableExecution execution) {TransitionImpl transition = execution.getTransition();List<ExecutionListener> executionListeners = transition.getExecutionListeners();int executionListenerIndex = execution.getExecutionListenerIndex();if (executionListeners.size()>executionListenerIndex) {execution.setEventName(org.activiti.engine.impl.pvm.PvmEvent.EVENTNAME_TAKE);execution.setEventSource(transition);ExecutionListener listener = executionListeners.get(executionListenerIndex);try {listener.notify(execution);} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new PvmException("couldn't execute event listener : "+e.getMessage(), e);}execution.setExecutionListenerIndex(executionListenerIndex+1);execution.performOperation(this);} else {execution.setExecutionListenerIndex(0);execution.setEventName(null);execution.setEventSource(null);ActivityImpl activity = (ActivityImpl) execution.getActivity();ActivityImpl nextScope = findNextScope(activity.getParent(), transition.getDestination());execution.setActivity(nextScope);if(Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createSequenceFlowTakenEvent(ActivitiEventType.SEQUENCEFLOW_TAKEN, transition.getId(),activity.getId(), (String) activity.getProperties().get("name") ,(String) activity.getProperties().get("type"), activity.getActivityBehavior().getClass().getCanonicalName(),nextScope.getId(), (String) nextScope.getProperties().get("name"), (String) nextScope.getProperties().get("type"), nextScope.getActivityBehavior().getClass().getCanonicalName()));}execution.performOperation(TRANSITION_CREATE_SCOPE);}}/** finds the next scope to enter.  the most outer scope is found first */public static ActivityImpl findNextScope(ScopeImpl outerScopeElement, ActivityImpl destination) {ActivityImpl nextScope = destination;while( (nextScope.getParent() instanceof ActivityImpl)&& (nextScope.getParent() != outerScopeElement)) {nextScope = (ActivityImpl) nextScope.getParent();}return nextScope;}
}

8-23行嵌套调用,遍历每一个执行监听器触发take事件。30-32行把execution的当前活动设置为下一个活动节点。34-38行触发全局事件转发器SEQUENCEFLOW_TAKEN事件。从此execution进入下一个节点。

 

小结

普遍来说,流程从一般的活动离开,就是通过ExecutionEntity的take方法,然后经历数个原子操作,触发执行监听器和全局事件转发器对应的事件,如果涉及子流程、网关形成的分支流程等,则情况会更复杂一些。最后把该触发的事件都触发完毕之后,把ExecutionEntity当前的活动设置为下一个节点,就此进入下一个节点相关的活动中。

这篇关于activiti学习(二十)——流程虚拟机源码分析(二)——从开始节点离开到下个节点前的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/279748

相关文章

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结