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-definitions | true / false | 是否自动检查 processes/ 目录下的流程定义文件 |
process-definition-location-prefix | classpath*:/processes/ | 流程定义文件(BPMN)的加载路径 |
2.1.2、特点
- 简化部署操作,减少手动干预
- 适用于流程定义相对固定的场景
- 启动应用时自动完成部署
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流程部署涉及数据表
- ACT_GE_PROPERTY:存储引擎属性和配置信息(部署时会查询该表进行相关判断)
- ACT_RE_PROCDEF:存储流程定义信息
- ACT_RE_DEPLOYMENT: 存储部署信息。
- 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();
}