Activiti启动流程源码解析

Activit启动流程主要有两种方式:
1、利用FormService启动:

public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map<String, String> properties) {
        return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, businessKey, properties));
    }

2、利用RuntimeService启动:

public ProcessInstance startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables) {
        return (ProcessInstance)this.commandExecutor.execute(new StartProcessInstanceCmd((String)null, processDefinitionId, businessKey, variables));
    }

两者有个重要区别:
formService.submitStartFormData方法的第三个参数是限定了Map<String, String>类型的变量,而runtimeService.startProcessInstanceById方法的第三个参数类型为Map<String, Object>。这就意味着后者的参数类型适用范围更广,如果需要传递除String类型的变量外时推荐使用后者(如在发起流程后,紧接着一个会签节点,而此时需要传递assigneeList办理人数据变量就只能用后者启动了)。
此外,前者主要还是用于动态表单、外置表单类型的流程启动。后者用于普通表单类型的流程启动。两者本质区别是:前者会去解析表单字段,而后者不会,其他流程基本一致。
本篇将首先去解析formService.submitStartFormData启动流程的方法源码。
1、该启动方法共有两种重载形式:

    public ProcessInstance submitStartFormData(String processDefinitionId, Map<String, String> properties) {
        return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, (String)null, properties));
    }

    public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map<String, String> properties) {
        return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, businessKey, properties));
    }

区别仅在于是否需要传递businessKey参数(一般而言做实际业务时都是需要的),因此我们着重看第二种形式。

public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map<String, String> properties) {
        return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, businessKey, properties));
    }

Activiti是一种典型的责任链+命令的设计模式,使得新手比较难于跟踪源码,建议有时间的话先去学习下这两种设计模式。但是这种设计的好处也很明显,使得我们在对Activiti进行功能需求自定义或者扩展时显得异常方便,以后有空我会专门写一篇Activiti自定义命令重写其原生方法的文章。
好了,废话不多说,这里面具体的实现者是SubmitStartFormCmd这个命令类,跟进去看源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.activiti.engine.impl.cmd;

import java.util.Map;
import org.activiti.engine.impl.form.StartFormHandler;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.runtime.ProcessInstance;

public class SubmitStartFormCmd extends NeedsActiveProcessDefinitionCmd<ProcessInstance> {
    private static final long serialVersionUID = 1L;
    protected final String businessKey;
    protected Map<String, String> properties;

    public SubmitStartFormCmd(String processDefinitionId, String businessKey, Map<String, String> properties) {
        super(processDefinitionId);
        this.businessKey = businessKey;
        this.properties = properties;
    }

    protected ProcessInstance execute(CommandContext commandContext, ProcessDefinitionEntity processDefinition) {
        ExecutionEntity processInstance = null;
        if (this.businessKey != null) {
            processInstance = processDefinition.createProcessInstance(this.businessKey);
        } else {
            processInstance = processDefinition.createProcessInstance();
        }

        commandContext.getHistoryManager().reportFormPropertiesSubmitted(processInstance, this.properties, (String)null);
        StartFormHandler startFormHandler = processDefinition.getStartFormHandler();
        startFormHandler.submitFormProperties(this.properties, processInstance);
        processInstance.start();
        return processInstance;
    }
}

这个命令类只有一个构造方法和一个execute方法,重点就是这个execute方法了:

protected ProcessInstance execute(CommandContext commandContext, ProcessDefinitionEntity processDefinition) {
        ExecutionEntity processInstance = null;
        if (this.businessKey != null) {//这里即是对两种重载方法的兼容处理
            processInstance = processDefinition.createProcessInstance(this.businessKey);
        } else {
            processInstance = processDefinition.createProcessInstance();
        }

        commandContext.getHistoryManager().reportFormPropertiesSubmitted(processInstance, this.properties, (String)null);
        StartFormHandler startFormHandler = processDefinition.getStartFormHandler();
        startFormHandler.submitFormProperties(this.properties, processInstance);
        processInstance.start();
        return processInstance;
    }

看下这步:

processInstance = processDefinition.createProcessInstance(this.businessKey);

其源码:

   public ExecutionEntity createProcessInstance(String businessKey) {
       return this.createProcessInstance(businessKey, (ActivityImpl)null);
   }

继续:

   public ExecutionEntity createProcessInstance(String businessKey, ActivityImpl initial) {
        ExecutionEntity processInstance = null;
        if (initial == null) {
            processInstance = (ExecutionEntity)super.createProcessInstance();
        } else {
            processInstance = (ExecutionEntity)super.createProcessInstanceForInitial(initial);
        }

        processInstance.setExecutions(new ArrayList());
        processInstance.setProcessDefinition(this.processDefinition);
        if (businessKey != null) {
            processInstance.setBusinessKey(businessKey);
        }

        if (this.getTenantId() != null) {
            processInstance.setTenantId(this.getTenantId());
        }

        processInstance.setProcessInstance(processInstance);
        Map<String, Object> dataObjectVars = this.getVariables();
        if (dataObjectVars != null) {
            processInstance.setVariables(dataObjectVars);
        }

        String authenticatedUserId = Authentication.getAuthenticatedUserId();
        String initiatorVariableName = (String)this.getProperty("initiatorVariableName");
        if (initiatorVariableName != null) {
            processInstance.setVariable(initiatorVariableName, authenticatedUserId);
        }

        if (authenticatedUserId != null) {
            processInstance.addIdentityLink(authenticatedUserId, (String)null, "starter");
        }

        Context.getCommandContext().getHistoryManager().recordProcessInstanceStart(processInstance);
        if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
            Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, processInstance));
        }

        return processInstance;
    }

显然,这个方法的目的就是构造一个流程实例对象ExecutionEntity,都是一些属性的赋值,其中有一点需要注意下:

 String authenticatedUserId = Authentication.getAuthenticatedUserId();
  if (authenticatedUserId != null) {
            processInstance.addIdentityLink(authenticatedUserId, (String)null, "starter");
        }

其中Authentication.getAuthenticatedUserId()这个是在哪里取的流程启动的用户ID的呢?看其源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.activiti.engine.impl.identity;

public abstract class Authentication {
    static ThreadLocal<String> authenticatedUserIdThreadLocal = new ThreadLocal();

    public Authentication() {
    }

    public static void setAuthenticatedUserId(String authenticatedUserId) {
        authenticatedUserIdThreadLocal.set(authenticatedUserId);
    }

    public static String getAuthenticatedUserId() {
        return (String)authenticatedUserIdThreadLocal.get();
    }
}

原来这个类就是拿来存放线程的局部变量的,Activiti用于存放办理用户的信息,便于随时取出使用,刚才我们已经看到了流程在启动时会去取出它的值作为流程的启动用户,那这个值是什么时候放进去的呢?答案是:在调用启动流程的方法之前,需要先调用下面这句代码:

// 流程与用户ID绑定
identityService.setAuthenticatedUserId(in.getUserId().toString());

好了,这就很清晰了。我们接着createProcessInstance看这个方法:

if (authenticatedUserId != null) {
            processInstance.addIdentityLink(authenticatedUserId, (String)null, "starter");
        }

(关于addIdentityLink的源码后面有空会单独分析,这里不展开叙述)这一步就是说如果启动用户取得出来,就在act_ru_identitylink和act_hi_identitylink中插入一条数据并标记为流程的“starter”。

Context.getCommandContext().getHistoryManager().recordProcessInstanceStart(processInstance);

这个就是往流程的act_hi_comment表里塞条数据,记录流程启动的相关信息。

if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
            Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, processInstance));
        }

这个就是流程启动后会分发一个事件,这个事件可以在监听器中拦截到。至此,这个processDefinition.createProcessInstance(this.businessKey)方法我们就分析完毕了,就是新建了个流程实例对象,并在act_ru_identitylink、act_hi_identitylink表里记录了流程启动的用户信息和在act_hi_comment表里记录了流程的启动相关信息;
接着:

commandContext.getHistoryManager().reportFormPropertiesSubmitted(processInstance, this.properties, (String)null);

看源码:

public void reportFormPropertiesSubmitted(ExecutionEntity processInstance, Map<String, String> properties, String taskId) {
        if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
            Iterator var4 = properties.keySet().iterator();

            while(var4.hasNext()) {
                String propertyId = (String)var4.next();
                String propertyValue = (String)properties.get(propertyId);
                HistoricFormPropertyEntity historicFormProperty = new HistoricFormPropertyEntity(processInstance, propertyId, propertyValue, taskId);
                this.getDbSqlSession().insert(historicFormProperty);
            }
        }

    }

意思是遍历传进来的Map变量把他们存在act_hi_detail表里;接着:

StartFormHandler startFormHandler = processDefinition.getStartFormHandler();
startFormHandler.submitFormProperties(this.properties, processInstance);\

看submitFormProperties的源码:

public void submitFormProperties(Map<String, String> properties, ExecutionEntity execution) {
        Map<String, String> propertiesCopy = new HashMap(properties);
        Iterator var4 = this.formPropertyHandlers.iterator();

        while(var4.hasNext()) {
            FormPropertyHandler formPropertyHandler = (FormPropertyHandler)var4.next();
            formPropertyHandler.submitFormProperty(execution, propertiesCopy);
        }

        var4 = propertiesCopy.keySet().iterator();

        while(var4.hasNext()) {
            String propertyId = (String)var4.next();
            execution.setVariable(propertyId, propertiesCopy.get(propertyId));
        }

    }

这步:Map<String, String> propertiesCopy = new HashMap(properties);
把变量复制到新的Map中,目的是:防止持有老对象的引用,操作时改变了原有变量值。

while(var4.hasNext()) {
            FormPropertyHandler formPropertyHandler = (FormPropertyHandler)var4.next();
            formPropertyHandler.submitFormProperty(execution, propertiesCopy);
        }

看submitFormProperty源码:

public void submitFormProperty(ExecutionEntity execution, Map<String, String> properties) {
       if (!this.isWritable && properties.containsKey(this.id)) {
           throw new ActivitiException("form property '" + this.id + "' is not writable");
       } else if (this.isRequired && !properties.containsKey(this.id) && this.defaultExpression == null) {
           throw new ActivitiException("form property '" + this.id + "' is required");
       } else {
           boolean propertyExits = false;
           Object modelValue = null;
           if (properties.containsKey(this.id)) {
               propertyExits = true;
               String propertyValue = (String)properties.remove(this.id);
               if (this.type != null) {
                   modelValue = this.type.convertFormValueToModelValue(propertyValue);
               } else {
                   modelValue = propertyValue;
               }
           } else if (this.defaultExpression != null) {
               Object expressionValue = this.defaultExpression.getValue(execution);
               if (this.type != null && expressionValue != null) {
                   modelValue = this.type.convertFormValueToModelValue(expressionValue.toString());
               } else if (expressionValue != null) {
                   modelValue = expressionValue.toString();
               } else if (this.isRequired) {
                   throw new ActivitiException("form property '" + this.id + "' is required");
               }
           }

           if (propertyExits || modelValue != null) {
               if (this.variableName != null) {
                   execution.setVariable(this.variableName, modelValue);
               } else if (this.variableExpression != null) {
                   this.variableExpression.setValue(modelValue, execution);
               } else {
                   execution.setVariable(this.id, modelValue);
               }
           }

       }
   }

这里主要是activiti对变量进行内置表单类型解析,其中此版本的Activiti只支持这么几种表单字段类型:


TIM截图20180705162238.png

然后利用如下代码:

if (propertyExits || modelValue != null) {
                if (this.variableName != null) {
                    execution.setVariable(this.variableName, modelValue);
                } else if (this.variableExpression != null) {
                    this.variableExpression.setValue(modelValue, execution);
                } else {
                    execution.setVariable(this.id, modelValue);
                }
            }

会将变量存在act_ru_variable、act_hi_varinst中。

protected void updateVariableInstance(VariableInstanceEntity variableInstance, Object value, ExecutionEntity sourceActivityExecution) {
        VariableTypes variableTypes = Context.getProcessEngineConfiguration().getVariableTypes();
        VariableType newType = variableTypes.findVariableType(value);
        if (variableInstance != null && !variableInstance.getType().equals(newType)) {
            variableInstance.setValue((Object)null);
            variableInstance.setType(newType);
            variableInstance.forceUpdate();
            variableInstance.setValue(value);
        } else {
            variableInstance.setValue(value);
        }

        Context.getCommandContext().getHistoryManager().recordHistoricDetailVariableCreate(variableInstance, sourceActivityExecution, this.isActivityIdUsedForDetails());
        Context.getCommandContext().getHistoryManager().recordVariableUpdate(variableInstance);
    }

其中recordHistoricDetailVariableCreate源码:

public void recordHistoricDetailVariableCreate(VariableInstanceEntity variable, ExecutionEntity sourceActivityExecution, boolean useActivityId) {
        if (this.isHistoryLevelAtLeast(HistoryLevel.FULL)) {
            HistoricDetailVariableInstanceUpdateEntity historicVariableUpdate = HistoricDetailVariableInstanceUpdateEntity.copyAndInsert(variable);
            if (useActivityId && sourceActivityExecution != null) {
                HistoricActivityInstanceEntity historicActivityInstance = this.findActivityInstance(sourceActivityExecution);
                if (historicActivityInstance != null) {
                    historicVariableUpdate.setActivityInstanceId(historicActivityInstance.getId());
                }
            }
        }

    }

recordVariableUpdate源码:

public void recordVariableUpdate(VariableInstanceEntity variable) {
        if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
            HistoricVariableInstanceEntity historicProcessVariable = (HistoricVariableInstanceEntity)this.getDbSqlSession().findInCache(HistoricVariableInstanceEntity.class, variable.getId());
            if (historicProcessVariable == null) {
                historicProcessVariable = Context.getCommandContext().getHistoricVariableInstanceEntityManager().findHistoricVariableInstanceByVariableInstanceId(variable.getId());
            }

            if (historicProcessVariable != null) {
                historicProcessVariable.copyValue(variable);
            } else {
                HistoricVariableInstanceEntity.copyAndInsert(variable);
            }
        }

    }

submitFormProperties方法粗略分析到这儿了,最后:

processInstance.start();
return processInstance;

启动:

  public void start() {
        if (this.startingExecution == null && this.isProcessInstanceType()) {
            this.startingExecution = new StartingExecution(this.processDefinition.getInitial());
        }

        this.performOperation(AtomicOperation.PROCESS_START);
    }

performOperation源码:

public void performOperation(AtomicOperation executionOperation) {
        if (executionOperation.isAsync(this)) {
            this.scheduleAtomicOperationAsync(executionOperation);
        } else {
            this.performOperationSync(executionOperation);
        }

    }

scheduleAtomicOperationAsync这个是异步job方法,看下performOperationSync源码:

protected void performOperationSync(AtomicOperation executionOperation) {
        Context.getCommandContext().performOperation(executionOperation, this);
    }

继续:

    public void performOperation(AtomicOperation executionOperation, InterpretableExecution execution) {
        this.nextOperations.add(executionOperation);
        if (this.nextOperations.size() == 1) {
            try {
                Context.setExecutionContext(execution);

                while(!this.nextOperations.isEmpty()) {
                    AtomicOperation currentOperation = (AtomicOperation)this.nextOperations.removeFirst();
                    if (log.isTraceEnabled()) {
                        log.trace("AtomicOperation: {} on {}", currentOperation, this);
                    }

                    if (execution.getReplacedBy() == null) {
                        currentOperation.execute(execution);
                    } else {
                        currentOperation.execute(execution.getReplacedBy());
                    }
                }
            } finally {
                Context.removeExecutionContext();
            }
        }

currentOperation.execute(execution)源码:

public void execute(InterpretableExecution execution) {
        ActivityImpl activity = (ActivityImpl)execution.getActivity();
        ActivityBehavior activityBehavior = activity.getActivityBehavior();
        if (activityBehavior == null) {
            throw new PvmException("no behavior specified in " + activity);
        } else {
            log.debug("{} executes {}: {}", new Object[]{execution, activity, activityBehavior.getClass().getName()});

            try {
                if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
                    Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createActivityEvent(ActivitiEventType.ACTIVITY_STARTED, execution.getActivity().getId(), (String)execution.getActivity().getProperty("name"), execution.getId(), execution.getProcessInstanceId(), execution.getProcessDefinitionId(), (String)activity.getProperties().get("type"), activity.getActivityBehavior().getClass().getCanonicalName()));
                }

                activityBehavior.execute(execution);
            } catch (RuntimeException var5) {
                throw var5;
            } catch (Exception var6) {
                LogMDC.putMDCExecution(execution);
                throw new PvmException("couldn't execute activity <" + activity.getProperty("type") + " id=\"" + activity.getId() + "\" ...>: " + var6.getMessage(), var6);
            }
        }
    }

这一步 activityBehavior.execute(execution);主要是Activiti设定的多种节点类型的执行行为细节,这里不作展开分析了,因为……实在太多了。


TIM截图20180705171641.png

上图为ActivityBehavior类型图(部分)。
最后,Activiti将启动流程生成的实例返回。从这里可见,虽然在启动流程时,我们只需要一两行代码,但Activiti内部其实做的事情非常非常多,足以可见,Activiti这个框架设计的还是非常精巧的,致敬下原作者们!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容