Activit启动流程主要有两种方式:
1、利用FormService启动:
public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map properties) {
return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, businessKey, properties));
}
2、利用RuntimeService启动:
public ProcessInstance startProcessInstanceById(String processDefinitionId, String businessKey, Map variables) {
return (ProcessInstance)this.commandExecutor.execute(new StartProcessInstanceCmd((String)null, processDefinitionId, businessKey, variables));
}
两者有个重要区别:
formService.submitStartFormData方法的第三个参数是限定了Map
此外,前者主要还是用于动态表单、外置表单类型的流程启动。后者用于普通表单类型的流程启动。两者本质区别是:前者会去解析表单字段,而后者不会,其他流程基本一致。
本篇将首先去解析formService.submitStartFormData启动流程的方法源码。
1、该启动方法共有两种重载形式:
public ProcessInstance submitStartFormData(String processDefinitionId, Map properties) {
return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, (String)null, properties));
}
public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map properties) {
return (ProcessInstance)this.commandExecutor.execute(new SubmitStartFormCmd(processDefinitionId, businessKey, properties));
}
区别仅在于是否需要传递businessKey参数(一般而言做实际业务时都是需要的),因此我们着重看第二种形式。
public ProcessInstance submitStartFormData(String processDefinitionId, String businessKey, Map 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 {
private static final long serialVersionUID = 1L;
protected final String businessKey;
protected Map properties;
public SubmitStartFormCmd(String processDefinitionId, String businessKey, Map 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 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 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 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 properties, ExecutionEntity execution) {
Map 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
把变量复制到新的Map中,目的是:防止持有老对象的引用,操作时改变了原有变量值。
while(var4.hasNext()) {
FormPropertyHandler formPropertyHandler = (FormPropertyHandler)var4.next();
formPropertyHandler.submitFormProperty(execution, propertiesCopy);
}
看submitFormProperty源码:
public void submitFormProperty(ExecutionEntity execution, Map 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只支持这么几种表单字段类型:
然后利用如下代码:
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设定的多种节点类型的执行行为细节,这里不作展开分析了,因为……实在太多了。
上图为ActivityBehavior类型图(部分)。
最后,Activiti将启动流程生成的实例返回。从这里可见,虽然在启动流程时,我们只需要一两行代码,但Activiti内部其实做的事情非常非常多,足以可见,Activiti这个框架设计的还是非常精巧的,致敬下原作者们!