flowable部署和啓動源碼解析

flowable的部署有好幾種.現在舉例常用的方式:

Deployment deployment = repositoryService.createDeployment().name(bpmName).addInputStream(fileName, fileInputStream).deploy();

這裏分兩步先創建Deployment.然後再deploy.createDeployment()源碼如下:

@Override
public DeploymentBuilder createDeployment() {
        return commandExecutor.execute(new Command<DeploymentBuilder>() {
            @Override
            public DeploymentBuilder execute(CommandContext commandContext) {
                return new DeploymentBuilderImpl(RepositoryServiceImpl.this);
            }
        });
}

這裏是使用了建造者模式創建了一個DeploymentBuilder對象.

然後第二步是deploy.第一步中創建了一個DeploymentBuilder對象和RepositoryServiceImpl對象.部署就是通過給RepositoryServiceImpl對象注入DeploymentBuilder對象來實現的.代碼如下.

@Override
public Deployment deploy() {
        return repositoryService.deploy(this);
}

實現方法如下

public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
        return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}

可以看到又是命令模式,我們看一下DeployCmd命令:部署分四步

1.插入表act_re_deployment.

2.設置部署參數,有兩個isBpmn20XsdValidationEnabled和isProcessValidationEnabled都是true,都是校驗.

3.實際部署.EngineDeployer,其中實際處理部署邏輯的是BpmnDeployer.這裏會進入到第一個if內,找到舊的,設置爲1或者舊版本+1.並且插入流程定義表act_re_procdef 中.關鍵源碼如下.

if (deployment.isNew()) {
            if (!deploymentSettings.containsKey(DeploymentSettings.IS_DERIVED_DEPLOYMENT)) {
                Map<ProcessDefinitionEntity, ProcessDefinitionEntity> mapOfNewProcessDefinitionToPreviousVersion = getPreviousVersionsOfProcessDefinitions(parsedDeployment);
                setProcessDefinitionVersionsAndIds(parsedDeployment, mapOfNewProcessDefinitionToPreviousVersion);
                persistProcessDefinitionsAndAuthorizations(parsedDeployment);
                updateTimersAndEvents(parsedDeployment, mapOfNewProcessDefinitionToPreviousVersion);

            } else {
                Map<ProcessDefinitionEntity, ProcessDefinitionEntity> mapOfNewProcessDefinitionToPreviousDerivedVersion = 
                                getPreviousDerivedFromVersionsOfProcessDefinitions(parsedDeployment);
                setDerivedProcessDefinitionVersionsAndIds(parsedDeployment, mapOfNewProcessDefinitionToPreviousDerivedVersion, deploymentSettings);
                persistProcessDefinitionsAndAuthorizations(parsedDeployment);
            }
          
} else {
           makeProcessDefinitionsConsistentWithPersistedVersions(parsedDeployment);
}

4.發佈創建實體事件.至此部署完畢.整個部署的關鍵代碼如下:

org.flowable.engine.impl.cmd.DeployCmd#executeDeploy

 // Save the data
        CommandContextUtil.getDeploymentEntityManager(commandContext).insert(deployment);

        if (processEngineConfiguration.getEventDispatcher().isEnabled()) {
            processEngineConfiguration.getEventDispatcher().dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_CREATED, deployment));
        }

        // Deployment settings
        Map<String, Object> deploymentSettings = new HashMap<>();
        deploymentSettings.put(DeploymentSettings.IS_BPMN20_XSD_VALIDATION_ENABLED, deploymentBuilder.isBpmn20XsdValidationEnabled());
        deploymentSettings.put(DeploymentSettings.IS_PROCESS_VALIDATION_ENABLED, deploymentBuilder.isProcessValidationEnabled());

        // Actually deploy
        processEngineConfiguration.getDeploymentManager().deploy(deployment, deploymentSettings);

        if (deploymentBuilder.getProcessDefinitionsActivationDate() != null) {
            scheduleProcessDefinitionActivation(commandContext, deployment);
        }

        if (processEngineConfiguration.getEventDispatcher().isEnabled()) {
            processEngineConfiguration.getEventDispatcher().dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_INITIALIZED, deployment));
        }

 

flowable啓動流程有兩種方式.最終都是執行了StartProcessInstanceCmd命令,我以流程key方式啓動來分析源碼,從啓動流程開始.

啓動流程方式:ByKey

processInstance = runtimeService.startProcessInstanceByKey(procDefKey, businessTable + ":" + businessId, vars);

 這個方法內部如下

commandExecutor.execute(new StartProcessInstanceCmd<ProcessInstance>(processDefinitionKey, null, businessKey, variables));

我們看到這裏使用了設計模式-命令模式,執行了一個StartProcessInstanceCmd命令.org.flowable.engine.impl.interceptor.CommandInvoker#execute方法就是獲取Context.把command的結果賦值給context.

final CommandContext commandContext = Context.getCommandContext();
    // Execute the command.
    // This will produce operations that will be put on the agenda.
    agenda.planOperation(new Runnable() {
    
        @Override
        public void run() {
                commandContext.setResult(command.execute(commandContext));
        }
     });

我們再看一下StartProcessInstanceCmd內部.org.flowable.engine.impl.cmd.StartProcessInstanceCmd#execute方法做了兩件事

1.根據CommandContext獲取流程引擎配置實現類ProcessEngineConfigurationImpl 

2.獲取流程定義

3.啓動流程實例

源碼如下:

@Override
public ProcessInstance execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        processInstanceHelper = processEngineConfiguration.getProcessInstanceHelper();
        ProcessDefinition processDefinition = getProcessDefinition(processEngineConfiguration);

        ProcessInstance processInstance = null;
        if (hasStartFormData()) {
            processInstance = handleProcessInstanceWithForm(commandContext, processDefinition, processEngineConfiguration);
        } else {
            processInstance = startProcessInstance(processDefinition);
        }

        return processInstance;
}

a.獲取流程引擎配置比較簡單,因爲啓動的時候引擎已經初始化過了.先獲取引擎配置

if (commandContext != null) {
       return (ProcessEngineConfigurationImpl) commandContext.getEngineConfigurations().get(EngineConfigurationConstants.KEY_PROCESS_ENGINE_CONFIG);
}

 EngineConfigurations有很多種,流程key啓動時只有三種.

String KEY_PROCESS_ENGINE_CONFIG = "cfg.processEngine";
    
String KEY_IDM_ENGINE_CONFIG = "cfg.idmEngine";
    
String KEY_FORM_ENGINE_CONFIG = "cfg.formEngine";

 

b.我們再看獲取流程定義,其實就是去數據庫找流程部署表的記錄.流程定義的時候會添加流程定義記錄到act_re_procdef表.那麼是流程引擎是怎麼找到表的呢?

這就需要一個流程引擎配置類ProcessEngineConfiguration,在它的實現類中,initEntityManagers()方法和initDataManagers()分別初始化了EntihtyManager和DataManager.ProcessEngineConfigurationImpl把初始化好的DataManager注入XXXXXEntityManagerImpl裏.DataManager的相當於mybatis的mapper類,定義了方法的接口.

看一下initEntityManagers()部分源碼,給XXXXXEntityManagerImpl注入初始化的DataManager

if (processDefinitionEntityManager == null) {
            processDefinitionEntityManager = new ProcessDefinitionEntityManagerImpl(this, processDefinitionDataManager);
        }

 

有了流程引擎的配置信息就能獲取到操作流程的數據庫配置

ProcessDefinitionEntityManager processDefinitionEntityManager = processEngineConfiguration.getProcessDefinitionEntityManager();

所有操作數據庫的接口都繼承了EntityManager<EntityImpl extends Entity>接口,而操作實際是使用了DataManager,ProcessEngineConfigurationImpl是流程引擎的初始化類,

 processDefinitionDataManager.findLatestProcessDefinitionByKey(processDefinitionKey);

 processDefinitionDataManager最總使用的是sqlsession查詢數據庫.

(ProcessDefinitionEntity) getDbSqlSession().selectOne("selectLatestProcessDefinitionByKey", processDefinitionKey);

c.接下來是啓動流程實例.上一步我們已經查詢到了流程定義.現在就是流程實例的創建.是通過ProcessInstanceHelper的

createAndStartProcessInstanceWithInitialFlowElement()方法創建的.

有關鍵幾個步驟:

1.創建流程實例(初始化,然後設置值),創建參與者信息act_ru_identitylink.發佈創建實體事件.

2.插入act_hi_procinst表

3.發佈創建實體事件和創建實體變量事件

4.創建第一個執行節點,插入到act_ru_execution表中.

5.更新運行時實例表ACT_RU_ACTINST

        // Create the process instance
        String initiatorVariableName = null;
        if (initialFlowElement instanceof StartEvent) {
            initiatorVariableName = ((StartEvent) initialFlowElement).getInitiator();
        }
        
        String tenantId = null;
        if (overrideDefinitionTenantId != null) {
            tenantId = overrideDefinitionTenantId;
        } else {
            tenantId = processDefinition.getTenantId();
        }

        ExecutionEntity processInstance = CommandContextUtil.getExecutionEntityManager(commandContext)
                .createProcessInstanceExecution(processDefinition, predefinedProcessInstanceId, businessKey, tenantId, 
                                initiatorVariableName, initialFlowElement.getId());
        
        processInstance.setName(processInstanceName);
        
        // Callbacks
        if (callbackId != null) {
            processInstance.setCallbackId(callbackId);
        }
        if (callbackType != null) {
            processInstance.setCallbackType(callbackType);
        }

        CommandContextUtil.getHistoryManager(commandContext).recordProcessInstanceStart(processInstance);

        boolean eventDispatcherEnabled = CommandContextUtil.getProcessEngineConfiguration().getEventDispatcher().isEnabled();
        if (eventDispatcherEnabled) {
            CommandContextUtil.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
                    FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.PROCESS_CREATED, processInstance));
        }

        processInstance.setVariables(processDataObjects(process.getDataObjects()));

        // Set the variables passed into the start command
        if (variables != null) {
            for (String varName : variables.keySet()) {
                processInstance.setVariable(varName, variables.get(varName));
            }
        }
        if (transientVariables != null) {
            for (String varName : transientVariables.keySet()) {
                processInstance.setTransientVariable(varName, transientVariables.get(varName));
            }
        }
        
        // Fire events
        if (eventDispatcherEnabled) {
            CommandContextUtil.getProcessEngineConfiguration().getEventDispatcher()
                    .dispatchEvent(FlowableEventBuilder.createEntityWithVariablesEvent(FlowableEngineEventType.ENTITY_INITIALIZED, processInstance, variables, false));
        }

        // Create the first execution that will visit all the process definition elements
        ExecutionEntity execution = CommandContextUtil.getExecutionEntityManager(commandContext).createChildExecution(processInstance);
        execution.setCurrentFlowElement(initialFlowElement);

        CommandContextUtil.getActivityInstanceEntityManager(commandContext).recordActivityStart(execution);

由於時間有限.最後這一步有點粗糙.下次會專門寫一個文章分析啓動時源碼.如有疑問歡迎留言

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章