3、Activiti-流程部署

1、引言:

Activiti作为一款流行的开源工作流引擎,提供了多种灵活的方式来部署业务流程定义(BPMN 2.0)。本文将介绍activiti中主要的流程部署方式。

2、Activiti流程部署方式

2.1、方式一:通过引入.yml或.properties配置文件配置avtiviti相关信息,并在项目启动时进行流程部署。这里以.yml文件格式为例,如下所示:

spring:
	activiti:
    	check-process-definitions: true
    	process-definition-location-prefix: classpath*:processes/
2.1.1、配置项及属性说明:
配置项属性值说明
check-process-definitionstrue / false是否自动检查 processes/ 目录下的流程定义文件
process-definition-location-prefixclasspath*:/processes/流程定义文件(BPMN)的加载路径
2.1.2、特点
  1. 简化部署操作,减少手动干预
  2. 适用于流程定义相对固定的场景
  3. 启动应用时自动完成部署

2.2、方式二:通过Classpath资源部署

要将流程部署逻辑封装到类中并在项目启动时自动执行,创建一个专门的部署服务类,并利用Spring的@PostConstruct注解或实现CommandLineRunner/ApplicationRunner接口来实现自动部署

2.2.1、方案一:使用@Service和@PostConstruct

/**
 * describe:方案一:使用@Service和@PostConstruct
 *
 * @author rqtanc
 * @date 2025/4/9 22:59
 */

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class ProcessDeploymentService {

    @Autowired
    private RepositoryService repositoryService;

    @PostConstruct
    public void deployProcesses() {
        try {
            Deployment deployment = repositoryService.createDeployment()
                    .addClasspathResource("processes/测试流程.bpmn") // 流程定义文件
                    //...其它 
                    .category("测试") // 设置分类 默认default
                    .key("testProcess") // 设置流程key
                    .name("测试流程部署") // 名称
                    .deploy();

            System.out.println("流程部署成功,部署ID: " + deployment.getId());
        } catch (Exception e) {
            System.err.println("流程部署失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
2.2.2、方案二:实现CommandLineRunner接口

/**
 * describe:实现CommandLineRunner接口
 *
 * @author rqtanc
 * @date 2025/4/9 23:15
 */

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class ProcessDeploymentRunner implements CommandLineRunner {

    private final RepositoryService repositoryService;

    public ProcessDeploymentRunner(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    @Override
    public void run(String... args) throws Exception {
        try {
            Deployment deployment = repositoryService.createDeployment()
                    .addClasspathResource("processes/测试流程.bpmn")
                    //...其它 
                    .category("测试")
                    .key("testProcess")
                    .name("测试流程部署")
                    .deploy();

            System.out.println("流程部署成功,部署ID: " + deployment.getId());
        } catch (Exception e) {
            System.err.println("流程部署失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
2.2.3、方案三:CommandLineRunner 接口 + 自定义服务类

自定义服务类:

/**
 * describe:CommandLineRunner 接口 + 自定义服务类
 *
 * @author rqtanc
 * @date 2025/4/9 23:46
 */

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class ActivitiDeploymentService {

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 部署流程定义
     * @param resources 资源路径数组
     * @param category 分类
     * @param key 流程key
     * @param name 部署名称
     * @return 部署ID
     */
    public String deployProcess(String[] resources, String category, String key, String name) {
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(resources[0])
                .category(category)
                .key(key)
                .name(name)
                .deploy();

        return deployment.getId();
    }

    /**
     * 检查流程是否已部署
     * @param processDefinitionKey 流程定义key
     * @return 是否已部署
     */
    public boolean isProcessDeployed(String processDefinitionKey) {
        long count = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .count();
        return count > 0;
    }
}

项目启动类:

/**
 * 启动程序
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class},scanBasePackages = {"com.hyf.**"})
@ComponentScan({"com.hyf.**"})
@EnableAsync
public class CrazyEasyApplication
{
	static {
	    System.setProperty("druid.mysql.usePingMethod","false");
	}
    public static void main(String[] args)
    {
        SpringApplication.run(CrazyEasyApplication.class, args);
        System.out.println( "启动成功");
    }
    // CommandLineRunner 接口
    @Bean
    public CommandLineRunner init(ActivitiDeploymentService deploymentService) {
        return args -> {
            if (!deploymentService.isProcessDeployed("testProcess")) {
                String[] resources = {
                        "processes/测试流程.bpmn"
                };
                deploymentService.deployProcess(resources, "测试", "testProcess", "测试流程部署");
            }
        };
    }
}

2.3、方式三:上传流程定义文件或zip压缩包的方式进行部署

Controller:

/**
 * describe:流程部署
 *
 * @author rqtanc
 * @date 2025/4/10 00:08
 */

@RestController
@RequestMapping("/process/definition")
public class ProcessDefinitionController{

	@Autowired
    private ProcessDefinitionService processDefinitionService;

	@PostMapping(value = "/deployment")
	    public AjaxResult deployment(@RequestParam("file") MultipartFile file) throws IOException {
        processDefinitionService.deployment(file);
        return AjaxResult.success();
    }
}

Service:

/**
 * describe:流程部署
 *
 * @author rqtanc
 * @date 2025/4/10 00:10
 */
public interface ProcessDefinitionService {
	void uploadStreamAndDeployment(MultipartFile file) throws IOException;
}

ServiceImpl:

/**
 * describe:流程部署
 *
 * @author rqtanc
 * @date 2025/4/10 00:10
 */
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;

@Service
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {
    
    @Autowired
    private RepositoryService repositoryService;
    
    @Override
    public void uploadStreamAndDeployment(MultipartFile file) throws IOException {
        // 获取上传的文件名
        String fileName = file.getOriginalFilename();
        // 得到输入流(字节流)对象
        InputStream fileInputStream = file.getInputStream();
        // 文件的扩展名
        String extension = FilenameUtils.getExtension(fileName);

        if (extension.equals("zip")) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            repositoryService.createDeployment()//初始化流程
                    .addZipInputStream(zip)
                    // 其它···
                    .deploy();
        } else {
            repositoryService.createDeployment()//初始化流程
                    .addInputStream(fileName, fileInputStream)
					// 其它···
                    .deploy();
        }
    }
}

3、Activiti流程部署涉及数据表

  1. ACT_GE_PROPERTY:存储引擎属性和配置信息(部署时会查询该表进行相关判断)
  2. ACT_RE_PROCDEF:存储流程定义信息
  3. ACT_RE_DEPLOYMENT: 存储部署信息。
  4. ACT_GE_BYTEARRAY: 存储字节数组数据。
    在这里插入图片描述

4、执行过程(源码分析)

ProcessEngineAutoConfiguration:自动配置类

    @Bean
    @ConditionalOnMissingBean
    public SpringProcessEngineConfiguration springProcessEngineConfiguration(
            DataSource dataSource,
            PlatformTransactionManager transactionManager,
            SpringAsyncExecutor springAsyncExecutor,
            ActivitiProperties activitiProperties,
            ProcessDefinitionResourceFinder processDefinitionResourceFinder,
            ProjectModelService projectModelService,
            @Autowired(required = false) List<ProcessEngineConfigurationConfigurer> processEngineConfigurationConfigurers,
            @Autowired(required = false) List<ProcessEngineConfigurator> processEngineConfigurators) throws IOException {

		//1、 配置对象创建
        SpringProcessEngineConfiguration conf = new SpringProcessEngineConfiguration(projectModelService);
        conf.setConfigurators(processEngineConfigurators);
        //2、配置流程定义资源
        configureProcessDefinitionResources(processDefinitionResourceFinder,
                                            conf);
        conf.setDataSource(dataSource);
        conf.setTransactionManager(transactionManager);

        conf.setAsyncExecutor(springAsyncExecutor);
        conf.setDeploymentName(activitiProperties.getDeploymentName());
        conf.setDatabaseSchema(activitiProperties.getDatabaseSchema());
        conf.setDatabaseSchemaUpdate(activitiProperties.getDatabaseSchemaUpdate());
        conf.setDbHistoryUsed(activitiProperties.isDbHistoryUsed());
        conf.setAsyncExecutorActivate(activitiProperties.isAsyncExecutorActivate());
        // 3、用于在特定条件下为流程引擎配置添加异步属性验证器
        addAsyncPropertyValidator(activitiProperties,
                                  conf);
        conf.setMailServerHost(activitiProperties.getMailServerHost());
        conf.setMailServerPort(activitiProperties.getMailServerPort());
        conf.setMailServerUsername(activitiProperties.getMailServerUserName());
        conf.setMailServerPassword(activitiProperties.getMailServerPassword());
        conf.setMailServerDefaultFrom(activitiProperties.getMailServerDefaultFrom());
        conf.setMailServerUseSSL(activitiProperties.isMailServerUseSsl());
        conf.setMailServerUseTLS(activitiProperties.isMailServerUseTls());

        if (userGroupManager != null) {
            conf.setUserGroupManager(userGroupManager);
        }

        conf.setHistoryLevel(activitiProperties.getHistoryLevel());
        conf.setCopyVariablesToLocalForTasks(activitiProperties.isCopyVariablesToLocalForTasks());
        conf.setSerializePOJOsInVariablesToJson(activitiProperties.isSerializePOJOsInVariablesToJson());
        conf.setJavaClassFieldForJackson(activitiProperties.getJavaClassFieldForJackson());

        if (activitiProperties.getCustomMybatisMappers() != null) {
            conf.setCustomMybatisMappers(getCustomMybatisMapperClasses(activitiProperties.getCustomMybatisMappers()));
        }

        if (activitiProperties.getCustomMybatisXMLMappers() != null) {
            conf.setCustomMybatisXMLMappers(new HashSet<>(activitiProperties.getCustomMybatisXMLMappers()));
        }

        if (activitiProperties.getCustomMybatisXMLMappers() != null) {
            conf.setCustomMybatisXMLMappers(new HashSet<>(activitiProperties.getCustomMybatisXMLMappers()));
        }

        if (activitiProperties.isUseStrongUuids()) {
            conf.setIdGenerator(new StrongUuidGenerator());
        }

        if (activitiProperties.getDeploymentMode() != null) {
            conf.setDeploymentMode(activitiProperties.getDeploymentMode());
        }

        if (processEngineConfigurationConfigurers != null) {
            for (ProcessEngineConfigurationConfigurer processEngineConfigurationConfigurer : processEngineConfigurationConfigurers) {
                processEngineConfigurationConfigurer.configure(conf);
            }
        }
        // 调用异步执行器
        springAsyncExecutor.applyConfig(conf);
        return conf;
    }

private void configureProcessDefinitionResources(ProcessDefinitionResourceFinder processDefinitionResourceFinder, SpringProcessEngineConfiguration conf) throws IOException {
   	//获取流程定义资源
       List<Resource> procDefResources = processDefinitionResourceFinder.discoverProcessDefinitionResources();
       if (!procDefResources.isEmpty()) {
       //调用配置对象的setDeploymentResources()方法设置要部署的资源
           conf.setDeploymentResources(procDefResources.toArray(new Resource[0]));
       }
   }
}

protected void addAsyncPropertyValidator(ActivitiProperties activitiProperties,SpringProcessEngineConfiguration conf) {
		// 检查配置属性中异步执行器是否未激活,只有在异步执行器未激活时才执行后续逻辑
        if (!activitiProperties.isAsyncExecutorActivate()) {
        	//创建一个新的验证器集合,命名为"activiti-spring-boot-starter"
            ValidatorSet springBootStarterValidatorSet = new ValidatorSet("activiti-spring-boot-starter");
            // 添加异步属性验证器
            springBootStarterValidatorSet.addValidator(new AsyncPropertyValidator());
            if (conf.getProcessValidator() == null) {
            	// 处理流程验证器不存在则创建一个新的ProcessValidatorImpl实例
                ProcessValidatorImpl processValidator = new ProcessValidatorImpl();
                // 将验证器集合添加到流程验证器中
                processValidator.addValidatorSet(springBootStarterValidatorSet);
                //将流程验证器设置到配置对象中
                conf.setProcessValidator(processValidator);
            } else {
                //获取现有的验证器集合列表,将新的验证器集合添加到现有集合中
				conf.getProcessValidator().getValidatorSets().add(springBootStarterValidatorSet);
            }
        }
    }

SpringProcessEngineConfiguration: 配置解析

public SpringProcessEngineConfiguration(ProjectModelService projectModelService) {
      //  设置事务由外部管理(通常是Spring框架),而不是由流程引擎内部管理
      this.transactionsExternallyManaged = true;
      // 创建默认的自动部署策略,使用传入的ProjectModelService
      defaultAutoDeploymentStrategy = new DefaultAutoDeploymentStrategy(projectModelService);
      // 将默认自动部署策略添加到部署策略集合中
      deploymentStrategies.add(defaultAutoDeploymentStrategy);
      // 添加单资源自动部署策略 - 处理单个流程资源文件的部署
      deploymentStrategies.add(new SingleResourceAutoDeploymentStrategy(projectModelService));
      // 添加资源父文件夹自动部署策略 - 基于资源所在文件夹进行部署
      deploymentStrategies.add(new ResourceParentFolderAutoDeploymentStrategy(projectModelService));
      // 添加"无流程时失败"自动部署策略 - 当没有找到流程定义时使部署失败
      deploymentStrategies.add(new FailOnNoProcessAutoDeploymentStrategy(projectModelService));
       // 添加"永不失败"自动部署策略 - 即使部署出现问题也不使部署失败
      deploymentStrategies.add(new NeverFailAutoDeploymentStrategy(projectModelService));
 }

ProcessDefinitionResourceFinder:是一个用于查找和定位流程定义资源的辅助类,负责扫描和发现流程定义文件(如 .bpmn、.bpmn20.xml 等),在应用程序的特定位置查找流程定义资源。

public List<Resource> discoverProcessDefinitionResources() throws IOException {
		// 初始化资源列表
        List<Resource> resources = new ArrayList<>();
        // 检查是否启用流程定义检查,即yml文件配置项check-process-definitions: true
        if (activitiProperties.isCheckProcessDefinitions()) {
        	//遍历配置中的所有流程定义文件后缀模式(如*.bpmn, *.bpmn20.xml等)
            for (String suffix : activitiProperties.getProcessDefinitionLocationSuffixes()) {
            	//构建完整路径并获取资源
                String path = activitiProperties.getProcessDefinitionLocationPrefix() + suffix;
                //使用resourceLoader获取匹配该模式的所有资源,将找到的资源添加到结果列表中
                resources.addAll(Arrays.asList(resourceLoader.getResources(path)));
            }
            if (resources.isEmpty()) {
                LOGGER.info("No process definitions were found for auto-deployment in the location `" + activitiProperties.getProcessDefinitionLocationPrefix() + "`");
            } else {
                List<String> resourcesNames = resources.stream().map(Resource::getFilename).collect(Collectors.toList());
                LOGGER.info("The following process definition files will be deployed: " + resourcesNames);
            }
        }
        //返回找到的所有流程定义资源
        return resources;
}

DefaultAsyncJobExecutor:异步作业执行器,负责处理工作流中的异步任务执行

//流程引擎配置中应用异步执行器的各项配置参数
public void applyConfig(ProcessEngineConfigurationImpl processEngineConfiguration){
      // 设置消息队列模式标志
    isMessageQueueMode = processEngineConfiguration.isAsyncExecutorIsMessageQueueMode();
    //应用线程池配置
    applyThreadPoolConfig(processEngineConfiguration);
    // 应用任务队列配置
    applyQueueConfig(processEngineConfiguration);
	// 设置定时任务获取等待时间(毫秒)
    defaultTimerJobAcquireWaitTimeInMillis = processEngineConfiguration.getAsyncExecutorDefaultTimerJobAcquireWaitTime();
     // 设置异步任务获取等待时间(毫秒)  
    defaultAsyncJobAcquireWaitTimeInMillis = processEngineConfiguration.getAsyncExecutorDefaultAsyncJobAcquireWaitTime();
	// 应用锁配置
    applyLockConfig(processEngineConfiguration);
 	// 设置重置过期任务的间隔时间
    resetExpiredJobsInterval = processEngineConfiguration.getAsyncExecutorResetExpiredJobsInterval();
    // 设置每次重置过期任务的分页大小    
    resetExpiredJobsPageSize = processEngineConfiguration.getAsyncExecutorResetExpiredJobsPageSize();
	// 设置关闭时的等待时间(秒)
    secondsToWaitOnShutdown = processEngineConfiguration.getAsyncExecutorSecondsToWaitOnShutdown();
    // 设置每次获取最大异步任务数量
    maxAsyncJobsDuePerAcquisition = processEngineConfiguration.getAsyncExecutorMaxAsyncJobsDuePerAcquisition();
    // 设置每次获取最大定时任务数量
    maxTimerJobsPerAcquisition = processEngineConfiguration.getAsyncExecutorMaxTimerJobsPerAcquisition();
	// 设置失败任务重试等待时间(毫秒)
    retryWaitTimeInMillis = processEngineConfiguration.getAsyncFailedJobWaitTime();
  }

AbstractProcessEngineAutoConfiguration:自动装配的抽象配置类:

//声明该方法返回一个由 Spring 管理的 Bean,Bean 名称默认为方法名 processEngine
@Bean
public ProcessEngineFactoryBean processEngine(SpringProcessEngineConfiguration configuration) {
	//调用父类(AbstractProcessEngineConfiguration)的 springProcessEngineBean 方法。由父类完成 ProcessEngineFactoryBean 的创建和配置。
   return super.springProcessEngineBean(configuration);
}

AbstractProcessEngineConfiguration:装配的抽象配置类

//该工厂 Bean 实现了 Spring 的 FactoryBean 接口,用于延迟创建实际的 ProcessEngine,返回已配置的工厂 Bean
public ProcessEngineFactoryBean springProcessEngineBean(SpringProcessEngineConfiguration configuration) {
  //创建一个新的 ProcessEngineFactoryBean 实例(工厂 Bean)
  ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean();
  processEngineFactoryBean.setProcessEngineConfiguration(configuration);
  return processEngineFactoryBean;
}

ProcessEngineFactoryBean:用于创建和销毁 ProcessEngine 实例的工厂类

public ProcessEngine getObject() throws Exception {
  	//调用配置流程引擎的表达式管理器
    configureExpressionManager();
    //配置外部事务管理
    configureExternallyManagedTransactions();
	// 检查流程引擎配置中的 beans 属性是否为 null
    if (processEngineConfiguration.getBeans() == null) {
      //创建一个 SpringBeanFactoryProxyMap 实例,并将其设置为流程引擎配置的 beans 属性
      processEngineConfiguration.setBeans(new SpringBeanFactoryProxyMap(applicationContext));
    }

    this.processEngine = processEngineConfiguration.buildProcessEngine();
    return this.processEngine;
}

SpringProcessEngineConfiguration

public ProcessEngine buildProcessEngine() {
    ProcessEngine processEngine = super.buildProcessEngine();
    ProcessEngines.setInitialized(true);
    autoDeployResources(processEngine);
    return processEngine;
}
protected void autoDeployResources(ProcessEngine processEngine) {
    if (deploymentResources != null && deploymentResources.length > 0) {
      final AutoDeploymentStrategy strategy = getAutoDeploymentStrategy(deploymentMode);
      strategy.deployResources(deploymentName, deploymentResources, processEngine.getRepositoryService());
    }
}
protected AutoDeploymentStrategy getAutoDeploymentStrategy(final String mode) {
      AutoDeploymentStrategy result = defaultAutoDeploymentStrategy;
      for (final AutoDeploymentStrategy strategy : deploymentStrategies) {
          if (strategy.handlesMode(mode)) {
              result = strategy;
              break;
          }
      }
      return result;
}

ProcessEngineConfigurationImpl :

public ProcessEngine buildProcessEngine() {
    init();
    ProcessEngineImpl processEngine = new ProcessEngineImpl(this);
    postProcessEngineInitialisation();

    return processEngine;
}

DefaultAutoDeploymentStrategy:

    public void deployResources(final String deploymentNameHint,
                                final Resource[] resources,
                                final RepositoryService repositoryService) {

        // Create a single deployment for all resources using the name hint as
        // the
        // literal name

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().enableDuplicateFiltering().name(deploymentNameHint);

        for (final Resource resource : resources) {
            final String resourceName = determineResourceName(resource);

            deploymentBuilder.addInputStream(resourceName,
                                             resource);
        }

        loadProjectManifest(deploymentBuilder).deploy();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值