activiti 生成流程实例

activiti中创建一个新的流程实例,有以下几种方式:根据processDefinitionId生成;根据processDefinitionKey生成;还可以通过消息的方式创建。创建流程实例的API定义在org.activiti.engine.RuntimeService中,以startProcessInstanceByXXX的方式向外提供使用。使用者只需要根据自身的需要调用相关的API创建就可以了。

    通常情况下,我们只要像如下这般编写调用代码就可以创建一个流程实例了:

//使用processDefinitionKey来生成流程实现
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(defineKey, vars);

//使用 processDefinitionId 来生成流程实现
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, vars);


由于提供了不同的API,那这些API之间有什么区别呢?那就从源码的角度去看看这些不同的接口下面隐藏着哪些不同。熟悉ACTIVITI的人都知道,avtiviti中大量使用命令模式实现相关的功能。根据definitionId和definitionKey生成流程实例的代码在类org.activiti.engine.impl.cmd.StartProcessInstanceCmd中定义。其核心代码如下:

  public ProcessInstance execute(CommandContext commandContext) {
    DeploymentManager deploymentCache = commandContext
      .getProcessEngineConfiguration()
      .getDeploymentManager();
    // Find the process definition
    ProcessDefinitionEntity processDefinition = null;
    if (processDefinitionId!=null) {
      processDefinition = deploymentCache.findDeployedProcessDefinitionById(processDefinitionId);
      if (processDefinition == null) {
        throw new ActivitiObjectNotFoundException("No process definition found for id = '" + processDefinitionId + "'", ProcessDefinition.class);
      }
    } else if (processDefinitionKey != null && (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(tenantId))){
      processDefinition = deploymentCache.findDeployedLatestProcessDefinitionByKey(processDefinitionKey);
      if (processDefinition == null) {
        throw new ActivitiObjectNotFoundException("No process definition found for key '" + processDefinitionKey +"'", ProcessDefinition.class);
      }
    } else if (processDefinitionKey != null && tenantId != null && !ProcessEngineConfiguration.NO_TENANT_ID.equals(tenantId)) {
     processDefinition = deploymentCache.findDeployedLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
       if (processDefinition == null) {
         throw new ActivitiObjectNotFoundException("No process definition found for key '" + processDefinitionKey +"' for tenant identifier " + tenantId, ProcessDefinition.class);
       }
    } else {
      throw new ActivitiIllegalArgumentException("processDefinitionKey and processDefinitionId are null");
    }
    // Do not start process a process instance if the process definition is suspended
    if (processDefinition.isSuspended()) {
      throw new ActivitiException("Cannot start process instance. Process definition " 
              + processDefinition.getName() + " (id = " + processDefinition.getId() + ") is suspended");
    }
    // Start the process instance
    ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
    // now set the variables passed into the start command
    if (variables != null) {
      processInstance.setVariables(variables);
    }
    // now set processInstance name
    if (processInstanceName != null) {
      processInstance.setName(processInstanceName);
    }
    processInstance.start();
    return processInstance;
  }

    上面的代码逻辑很简单,通过传入参数的不同,找到对应的流程定义,然后根据流程定义生成流程实例。最大的区别在于,由于部署同一个流程定义的时候会生成一个唯一的流程定义ID,故而根据ID查询流程定义的时候,能快速找到相应的流程定义。如果配置了合理的流程定义缓存策略,甚至可以直接在内存中查找到流程定义而不需要去数据库中去查询并生成;而根据流程定义KEY的方式需要去数据库中查询此KEY下的最新的流程定义。这部分的代码逻辑可以通过org.activiti.engine.impl.persistence.deploy.DeploymentManager类查看到详细逻辑:

//根据processDefinitionId查询流程定义
public ProcessDefinitionEntity findDeployedProcessDefinitionById(String processDefinitionId) {
    if (processDefinitionId == null) {
      throw new ActivitiIllegalArgumentException("Invalid process definition id : null");
    }
    
    // first try the cache
    ProcessDefinitionEntity processDefinition = processDefinitionCache.get(processDefinitionId);
    
    if (processDefinition == null) {
      processDefinition = Context.getCommandContext()
        .getProcessDefinitionEntityManager()
        .findProcessDefinitionById(processDefinitionId);
      if (processDefinition == null) {
        throw new ActivitiObjectNotFoundException("no deployed process definition found with id '" + processDefinitionId + "'", ProcessDefinition.class);
      }
      processDefinition = resolveProcessDefinition(processDefinition);
    }
    return processDefinition;
  }
  //根据processDefinitionKey查询流程定义
  public ProcessDefinitionEntity findDeployedLatestProcessDefinitionByKey(String processDefinitionKey) {
    ProcessDefinitionEntity processDefinition = Context
      .getCommandContext()
      .getProcessDefinitionEntityManager()
      .findLatestProcessDefinitionByKey(processDefinitionKey);
    
    if (processDefinition==null) {
      throw new ActivitiObjectNotFoundException("no processes deployed with key '"+processDefinitionKey+"'", ProcessDefinition.class);
    }
    processDefinition = resolveProcessDefinition(processDefinition);
    return processDefinition;
  }

    由于每次部署同一个流程定义文件,其版本号会自增,导致了流程定义ID发生改变,如果实际情况下需要使用指定版本的流程图(流程定义)来生成实例,则通过ById方式生成成了唯一的选择。其他情况下,为方便流程图更新后能快速生效,一般建议使用ByKey的方式生成实例。


    除了上述两种方式之外,还可以通过消息的方式创建实例,消息方式的实现与ById的方式类似的,只不过需要通过消息的方式获取Id,同时需要消息中获取activityId, 具体代码如下:

  public ProcessInstance execute(CommandContext commandContext) {
    if (messageName == null) {
      throw new ActivitiIllegalArgumentException("Cannot start process instance by message: message name is null");
    }
    MessageEventSubscriptionEntity messageEventSubscription = commandContext.getEventSubscriptionEntityManager()
          .findMessageStartEventSubscriptionByName(messageName, tenantId);
    
    if (messageEventSubscription == null) {
      throw new ActivitiObjectNotFoundException("Cannot start process instance by message: no subscription to message with name '"+messageName+"' found.", MessageEventSubscriptionEntity.class);
    }
    String processDefinitionId = messageEventSubscription.getConfiguration();
    if (processDefinitionId == null) {
      throw new ActivitiException("Cannot start process instance by message: subscription to message with name '"+messageName+"' is not a message start event.");
    }
    DeploymentManager deploymentCache = commandContext
            .getProcessEngineConfiguration()
            .getDeploymentManager();
    ProcessDefinitionEntity processDefinition = deploymentCache.findDeployedProcessDefinitionById(processDefinitionId);
    if (processDefinition == null) {
      throw new ActivitiObjectNotFoundException("No process definition found for id '" + processDefinitionId + "'", ProcessDefinition.class);
    }
    ActivityImpl startActivity = processDefinition.findActivity(messageEventSubscription.getActivityId());
    ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey, startActivity);
    if (processVariables != null) {
      processInstance.setVariables(processVariables);
    }
    processInstance.start();
    return processInstance;
  }