Bootstrap

flowable部署流程


前言

分析一下 flowable 的部署流程

一、CommandExecutor

在流程执行的过程中,都会经过 CommandExecutor 执行,所以先分析一下CommandExecutor

1、CommandExecutor 的初始化

在流程引擎初始化过程中

CommandExecutor -> init() -> initCommandExecutors() -> initCommandInterceptors() 和 initCommandExecutor()

2、initCommandInterceptors()

public void initCommandInterceptors() {
        if (commandInterceptors == null) {
            commandInterceptors = new ArrayList<>();
            if (customPreCommandInterceptors != null) {
                commandInterceptors.addAll(customPreCommandInterceptors);
            }
			//默认拦截器            
            commandInterceptors.addAll(getDefaultCommandInterceptors());
            if (customPostCommandInterceptors != null) {
                commandInterceptors.addAll(customPostCommandInterceptors);
            }
            //CommandInvoker 拦截器
            commandInterceptors.add(commandInvoker);
        }
    }
public Collection<? extends CommandInterceptor> getDefaultCommandInterceptors() {
        if (defaultCommandInterceptors == null) {
            List<CommandInterceptor> interceptors = new ArrayList<>();
            //日志拦截器
            interceptors.add(new LogInterceptor());

			//cockroachdb 重试拦截器
            if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) {
                interceptors.add(new CrDbRetryInterceptor());
            }
			
			//事务拦截器
            CommandInterceptor transactionInterceptor = createTransactionInterceptor();
            if (transactionInterceptor != null) {
                interceptors.add(transactionInterceptor);
            }

            if (commandContextFactory != null) {
                String engineCfgKey = getEngineCfgKey();
                //Command 上下文拦截器
                CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, 
                        classLoader, useClassForNameClassLoading, clock, objectMapper);
                engineConfigurations.put(engineCfgKey, this);
                commandContextInterceptor.setEngineCfgKey(engineCfgKey);
                commandContextInterceptor.setEngineConfigurations(engineConfigurations);
                interceptors.add(commandContextInterceptor);
            }

            if (transactionContextFactory != null) {
            	//transaction 上下文拦截器
                interceptors.add(new TransactionContextInterceptor(transactionContextFactory));
            }
			
			//其他拦截器,BpmnOverrideContextInterceptor
            List<CommandInterceptor> additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors();
            if (additionalCommandInterceptors != null) {
                interceptors.addAll(additionalCommandInterceptors);
            }

            defaultCommandInterceptors = interceptors;
        }
        return defaultCommandInterceptors;
    }

3、initCommandExecutor()

public void initCommandExecutor() {
        if (commandExecutor == null) {
        	//初始化了拦截器链
            CommandInterceptor first = initInterceptorChain(commandInterceptors);
            //传入拦截器链,创建执行器
            commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
        }
    }
public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
        if (chain == null || chain.isEmpty()) {
            throw new FlowableException("invalid command interceptor chain configuration: " + chain);
        }
        //设置setNext,构建执行器链
        for (int i = 0; i < chain.size() - 1; i++) {
            chain.get(i).setNext(chain.get(i + 1));
        }
        return chain.get(0);
    }

二、部署流程

1、示例

	@Test
    public void createDeployment() {
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("holiday-request.bpmn20.xml")
                .deploy();
        System.out.println(deployment.getId());
    }

2、createDeployment()

(1)createDeployment()

 @Override
    public DeploymentBuilder createDeployment() {
        return commandExecutor.execute(new Command<DeploymentBuilder>() {
        	//执行完拦截器链,回调当前方法,返回 DeploymentBuilderImpl 对象
            @Override
            public DeploymentBuilder execute(CommandContext commandContext) {
                return new DeploymentBuilderImpl(RepositoryServiceImpl.this);
            }
        });
    }

(2)执行拦截器链

 	@Override
    public <T> T execute(Command<T> command) {
        return execute(defaultConfig, command);
    }
	@Override
    public <T> T execute(CommandConfig config, Command<T> command) {
        return first.execute(config, command, this);
    }

(3)DeploymentBuilderImpl

 public DeploymentBuilderImpl(RepositoryServiceImpl repositoryService) {
 		//RepositoryServiceImpl
        this.repositoryService = repositoryService;
        //DeploymentEntityImpl
        this.deployment = CommandContextUtil.getProcessEngineConfiguration().getDeploymentEntityManager().create();
        //ResourceEntityManagerImpl
        this.resourceEntityManager = CommandContextUtil.getProcessEngineConfiguration().getResourceEntityManager();
    }

3、addClasspathResource(“holiday-request.bpmn20.xml”)

用于解析文件流

(1)addClasspathResource()

	@Override
    public DeploymentBuilder addClasspathResource(String resource) {
    	//获取文件流
        InputStream inputStream = ReflectUtil.getResourceAsStream(resource);
        if (inputStream == null) {
            throw new FlowableIllegalArgumentException("resource '" + resource + "' not found");
        }
        //处理文件流
        return addInputStream(resource, inputStream);
    }

(2)addInputStream()

	@Override
    public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
        if (inputStream == null) {
            throw new FlowableIllegalArgumentException("inputStream for resource '" + resourceName + "' is null");
        }
        //读取文件
        byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
        //创建 ResourceEntityImpl
        ResourceEntity resource = resourceEntityManager.create();
        resource.setName(resourceName);
        resource.setBytes(bytes);
        //将resource 放到 DeploymentEntityImpl 中
        deployment.addResource(resource);
        return this;
    }

4、deploy()

执行部署

(1)deploy()

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

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

在拦截器链的尽头,会回调 DeployCmd 的 execute() 方法

(2)DeployCmd.execute()

 	@Override
    public Deployment execute(CommandContext commandContext) {

        // Backwards compatibility with v5
        //flowable5 的逻辑
        if (deploymentBuilder.getDeploymentProperties() != null
                && deploymentBuilder.getDeploymentProperties().containsKey(DeploymentProperties.DEPLOY_AS_FLOWABLE5_PROCESS_DEFINITION)
                && deploymentBuilder.getDeploymentProperties().get(DeploymentProperties.DEPLOY_AS_FLOWABLE5_PROCESS_DEFINITION).equals(Boolean.TRUE)) {

            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
            if (processEngineConfiguration.isFlowable5CompatibilityEnabled() && processEngineConfiguration.getFlowable5CompatibilityHandler() != null) {
                return deployAsFlowable5ProcessDefinition(commandContext);
            } else {
                throw new FlowableException("Can't deploy a v5 deployment with no flowable 5 compatibility enabled or no compatibility handler on the classpath");
            }
        }
		//执行部署
        return executeDeploy(commandContext);
    }

(3)executeDeploy()

protected Deployment executeDeploy(CommandContext commandContext) {
        DeploymentEntity deployment = deploymentBuilder.getDeployment();

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        //设置部署时间
        deployment.setDeploymentTime(processEngineConfiguration.getClock().getCurrentTime());
		//如果开启了过滤重复的开关
        if (deploymentBuilder.isDuplicateFilterEnabled()) {
			
			//查询出来重复的 Deployment
            List<Deployment> existingDeployments = new ArrayList<>();
            if (deployment.getTenantId() == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(deployment.getTenantId())) {
                List<Deployment> deploymentEntities = new DeploymentQueryImpl(processEngineConfiguration.getCommandExecutor())
                        .deploymentName(deployment.getName())
                        .orderByDeploymentTime().desc()
                        .listPage(0, 1);
                if (!deploymentEntities.isEmpty()) {
                    existingDeployments.add(deploymentEntities.get(0));
                }
                
            } else {
                List<Deployment> deploymentList = processEngineConfiguration.getRepositoryService().createDeploymentQuery()
                        .deploymentName(deployment.getName())
                        .deploymentTenantId(deployment.getTenantId())
                        .orderByDeploymentTime().desc()
                        .listPage(0, 1);

                if (!deploymentList.isEmpty()) {
                    existingDeployments.addAll(deploymentList);
                }
            }

            if (!existingDeployments.isEmpty()) {
                DeploymentEntity existingDeployment = (DeploymentEntity) existingDeployments.get(0);
                //校验数据库的和当前的是否相同
                if (!deploymentsDiffer(deployment, existingDeployment)) {
                    return existingDeployment;
                }
            }
        }
		//新的部署流程
        deployment.setNew(true);

        // Save the data
        //保存到数据库
        processEngineConfiguration.getDeploymentEntityManager().insert(deployment);

        if (StringUtils.isEmpty(deployment.getParentDeploymentId())) {
            // If no parent deployment id is set then set the current ID as the parent
            // If something was deployed via this command than this deployment would
            // be a parent deployment to other potential child deployments
            deployment.setParentDeploymentId(deployment.getId());
        }

        FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
        if (eventDispatcher != null && eventDispatcher.isEnabled()) {
        	//发布事件 ENTITY_CREATED
            eventDispatcher.dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_CREATED, deployment),
                    processEngineConfiguration.getEngineCfgKey());
        }

        // 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
        //部署,解析bpmn文件等
        processEngineConfiguration.getDeploymentManager().deploy(deployment, deploymentSettings);

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

        if (eventDispatcher != null && eventDispatcher.isEnabled()) {
            eventDispatcher.dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_INITIALIZED, deployment),
                    processEngineConfiguration.getEngineCfgKey());
        }

        return deployment;
    }

(4)保存到数据库

实际上是先缓存起来

 	@Override
    public void insert(DeploymentEntity deployment) {
    	//保存 deployment
        insert(deployment, false);

		//保存 resource
        for (EngineResource resource : deployment.getResources().values()) {
            resource.setDeploymentId(deployment.getId());
            getResourceEntityManager().insert((ResourceEntity) resource);
        }
    }

保存的逻辑,是将数据对象先存储到 SqlSession 的缓存中

 	@Override
    public void insert(EntityImpl entity, boolean fireCreateEvent) {
        getDataManager().insert(entity);
        if (fireCreateEvent) {
            fireEntityInsertedEvent(entity);
        }
    }
	@Override
    public void insert(EntityImpl entity) {
        getDbSqlSession().insert(entity, getIdGenerator());
    }
public void insert(Entity entity, IdGenerator idGenerator) {
        if (entity.getId() == null) {
        	//生成id
            String id = idGenerator.getNextId();
            if (dbSqlSessionFactory.isUsePrefixId()) {
                id = entity.getIdPrefix() + id;
            }
            entity.setId(id);
        }
        
        Class<? extends Entity> clazz = entity.getClass();
        if (!insertedObjects.containsKey(clazz)) {
            insertedObjects.put(clazz, new LinkedHashMap<>()); // order of insert is important, hence LinkedHashMap
        }
		//存到 insertedObjects 中
        insertedObjects.get(clazz).put(entity.getId(), entity);
        entityCache.put(entity, false); // False -> entity is inserted, so always changed
        entity.setInserted(true);
    }

三、写入数据库

上面的流程是将数据存在了缓存中,真正写入数据库的操作是在 CommandContextInterceptor 的finally() 方法中,调用 commandContext.close(),会触发写入数据库的操作

1、finally

		finally {
            try {
                if (!contextReused) {
                    commandContext.close();
                }
                commandContext.setReused(originalContextReusedState);

            } 

2、 commandContext.close()

	public void close() {
		...
       flushSessions();
        ...           

3、 flushSessions()

	protected void flushSessions() {
        for (Session session : sessions.values()) {
            session.flush();
        }
    }

4、 DbSqlSession.flush()

 	@Override
    public void flush() {
        determineUpdatedObjects(); // Needs to be done before the removeUnnecessaryOperations, as removeUnnecessaryOperations will remove stuff from the cache
        removeUnnecessaryOperations();
		
		//打印日志
        if (LOGGER.isDebugEnabled()) {
            debugFlush();
        }
		
		//Insert
        flushInserts();
        //Update
        flushUpdates();
        //Delete
        flushDeletes();
    }

5、 flushInserts()

	protected void flushInserts() {
		
		//检查缓存要写入数据库的数据
        if (insertedObjects.size() == 0) {
            return;
        }
        
        // Handle in entity dependency order
        //按顺序写入数据库
        for (Class<? extends Entity> entityClass : dbSqlSessionFactory.getInsertionOrder()) {
            if (insertedObjects.containsKey(entityClass)) {
                flushInsertEntities(entityClass, insertedObjects.get(entityClass).values());
                insertedObjects.remove(entityClass);
            }
        }

        // Next, in case of custom entities or we've screwed up and forgotten some entity
        //其他自定义的对象写入数据库
        if (insertedObjects.size() > 0) {
            for (Class<? extends Entity> entityClass : insertedObjects.keySet()) {
                flushInsertEntities(entityClass, insertedObjects.get(entityClass).values());
            }
        }

        insertedObjects.clear();
    }

6、 flushInsertEntities()

protected void flushInsertEntities(Class<? extends Entity> entityClass, Collection<Entity> entitiesToInsert) {
        if (entitiesToInsert.size() == 1) {
        	//单条写入
            flushRegularInsert(entitiesToInsert.iterator().next(), entityClass);
        } else if (Boolean.FALSE.equals(dbSqlSessionFactory.isBulkInsertable(entityClass))) {
            for (Entity entity : entitiesToInsert) {
                flushRegularInsert(entity, entityClass);
            }
        } else {
        	//批量写入
            flushBulkInsert(entitiesToInsert, entityClass);
        }
    }

7、 flushRegularInsert()

protected void flushRegularInsert(Entity entity, Class<? extends Entity> clazz) {
        String insertStatement = dbSqlSessionFactory.getInsertStatement(entity);
        insertStatement = dbSqlSessionFactory.mapStatement(insertStatement);

        if (insertStatement == null) {
            throw new FlowableException("no insert statement for " + entity.getClass() + " in the ibatis mapping files");
        }

        LOGGER.debug("inserting: {}", entity);
        // insert 操作
        sqlSession.insert(insertStatement, entity);

        // See https://activiti.atlassian.net/browse/ACT-1290
        if (entity instanceof HasRevision) {
            incrementRevision(entity);
        }
    }

8、 insert()

到了 mybatis 的流程,执行插入数据库操作

  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }
	@Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

调用flowable自定义的mapper.xml文件执行sql

在这里插入图片描述

总结:

在流程部署中,涉及到的几个关键的表:

1、ACT_RE_PROCDEF 流程定义表
2、ACT_RE_DEPLOYMENT 流程部署表
3、ACT_GE_BYTEARRAY 流程资源表

;