当前位置: 首页 > news >正文

06 Activiti 与 Spring Boot 整合

前言

本文基于 Activiti 7.0.0.GA 版本的源码,研究 Activiti 是如何整合到 Spring Boot 中的。依据 Spring Boot 自动配置原理和自定义实现 Spring Boot Starter 等网络文章,快速找到 Activiti 的 starter 模块的 META-INF\spring.factories 文件,研究整合细节。

Spring Boot 版本

从 activiti-sprint-boot-starter 模块的 pom.xml 分析得知 Activiti 7.0.0.GA 基于 spring-boot-2.1.2.RELEASE 开发,这个 spring boot 版本于 2018年12月发布。

源码

按照 Spring Boot 2.0 系列自动配置约定,打开 META-INF\spring.factories 配置文件,

该文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.activiti.spring.boot.EndpointAutoConfiguration,\
org.activiti.spring.boot.ProcessEngineAutoConfiguration
文件中放入了两个自动配置类,分别是:
  • org.activiti.spring.boot.EndpointAutoConfiguration
  • org.activiti.spring.boot.ProcessEngineAutoConfiguration
接下来研究这两个类的实现细节。

EndpointAutoConfiguration

EndpointAutoConfiguration 自动配置类,官方给出的解释是:这个模块背后的想法是 Spring Security 可以根据需要与 org.activiti.engine.IdentityService 通信。

该配置类上只配置了 ProcessEngineEndpoint 这个 Bean。

package org.activiti.spring.boot;import org.activiti.engine.ProcessEngine;
import org.activiti.spring.boot.actuate.endpoint.ProcessEngineEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;/*** The idea behind this module is that Spring Security could* talk to the org.activiti.engine.IdentityService* as required.*/
@Configuration
@ComponentScan("org.activiti")public class EndpointAutoConfiguration {@Bean@ConditionalOnEnabledEndpointpublic ProcessEngineEndpoint processEngineEndpoint(ProcessEngine engine) {return new ProcessEngineEndpoint(engine);}
}

ProcessEngineEndpoint 类,注册一个 Boot Actuator 端点,该端点提供有关正在运行的流程实例的信息,并呈现已部署流程的 BPMN 图。

package org.activiti.spring.boot.actuate.endpoint;import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.persistence.deploy.DefaultDeploymentCache;
import org.activiti.engine.impl.persistence.deploy.DeploymentCache;
import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionCacheEntry;
import org.activiti.engine.repository.ProcessDefinition;import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** Registers a Boot Actuator endpoint that provides information on the* running process instance and renders BPMN diagrams of the deployed processes.* 翻译:注册一个 Boot Actuator 端点,该端点提供有关正在运行的流程实例的信息,并呈现已部署流程的 BPMN 图。*/
@ConfigurationProperties(prefix = "endpoints.activiti")
@Endpoint(id = "activiti")
public class ProcessEngineEndpoint {private final ProcessEngine processEngine;public ProcessEngineEndpoint(ProcessEngine processEngine) {this.processEngine = processEngine;}@ReadOperationpublic Map<String, Object> invoke() {Map<String, Object> metrics = new HashMap<String, Object>();// Process definitions// 统计 processDefinition 数量metrics.put("processDefinitionCount", processEngine.getRepositoryService().createProcessDefinitionQuery().count());// List of all process definitions// 列出所有的 processDefinition ,包括同一个 processDefinition 的不同版本。List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService().createProcessDefinitionQuery().orderByProcessDefinitionKey().asc().list();List<String> processDefinitionKeys = new ArrayList<String>();for (ProcessDefinition processDefinition : processDefinitions) {processDefinitionKeys.add(processDefinition.getKey() + " (v" + processDefinition.getVersion() + ")");}metrics.put("deployedProcessDefinitions", processDefinitionKeys);// Process instances// 统计 正在运行的流程实例数量Map<String, Object> processInstanceCountMap = new HashMap<String, Object>();metrics.put("runningProcessInstanceCount", processInstanceCountMap);for (ProcessDefinition processDefinition : processDefinitions) {processInstanceCountMap.put(processDefinition.getKey() + " (v" + processDefinition.getVersion() + ")",processEngine.getRuntimeService().createProcessInstanceQuery().processDefinitionId(processDefinition.getId()).count());}// 统计 已完成的流程实例数量Map<String, Object> completedProcessInstanceCountMap = new HashMap<String, Object>();metrics.put("completedProcessInstanceCount", completedProcessInstanceCountMap);for (ProcessDefinition processDefinition : processDefinitions) {completedProcessInstanceCountMap.put(processDefinition.getKey() + " (v" + processDefinition.getVersion() + ")",processEngine.getHistoryService().createHistoricProcessInstanceQuery().finished().processDefinitionId(processDefinition.getId()).count());}// Open tasks// 统计 处于运行状态的taskmetrics.put("openTaskCount",processEngine.getTaskService().createTaskQuery().count());// 统计 处于已完成状态的taskmetrics.put("completedTaskCount",processEngine.getHistoryService().createHistoricTaskInstanceQuery().finished().count());// Tasks completed today// 统计 今天(24小时)刚完成的taskmetrics.put("completedTaskCountToday",processEngine.getHistoryService().createHistoricTaskInstanceQuery().finished().taskCompletedAfter(new Date(System.currentTimeMillis() - secondsForDays(1))).count());// Process steps// 统计 处于已完成状态的 Activitiesmetrics.put("completedActivities",processEngine.getHistoryService().createHistoricActivityInstanceQuery().finished().count());// Process definition cache// 统计 缓存的ProcessDefinitionDeploymentCache<ProcessDefinitionCacheEntry> deploymentCache = ((ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration()).getProcessDefinitionCache();if (deploymentCache instanceof DefaultDeploymentCache) {metrics.put("cachedProcessDefinitionCount",((DefaultDeploymentCache) deploymentCache).size());}return metrics;}private long secondsForDays(int days) {int hour = 60 * 60 * 1000;int day = 24 * hour;return days * day;}
}

ProcessEngineAutoConfiguration

ProcessEngineAutoConfiguration 类关系图如下:

ProcessEngineAutoConfiguration 源码如下:

package org.activiti.spring.boot;// 省略 import@Configuration
@AutoConfigureAfter({DataSourceAutoConfiguration.class, TaskExecutionAutoConfiguration.class})
@EnableConfigurationProperties(ActivitiProperties.class)
public class ProcessEngineAutoConfiguration extends AbstractProcessEngineAutoConfiguration {private final UserGroupManager userGroupManager;public ProcessEngineAutoConfiguration(UserGroupManager userGroupManager) {this.userGroupManager = userGroupManager;}@Bean@ConditionalOnMissingBeanpublic SpringProcessEngineConfiguration springProcessEngineConfiguration(DataSource dataSource,PlatformTransactionManager transactionManager,SpringAsyncExecutor springAsyncExecutor,ActivitiProperties activitiProperties,ProcessDefinitionResourceFinder processDefinitionResourceFinder,@Autowired(required = false) ProcessEngineConfigurationConfigurer processEngineConfigurationConfigurer,@Autowired(required = false) List<ProcessEngineConfigurator> processEngineConfigurators) throws IOException {SpringProcessEngineConfiguration conf = new SpringProcessEngineConfiguration();conf.setConfigurators(processEngineConfigurators);configureProcessDefinitionResources(processDefinitionResourceFinder,conf);conf.setDataSource(dataSource);conf.setTransactionManager(transactionManager);if (springAsyncExecutor != null) {conf.setAsyncExecutor(springAsyncExecutor);}conf.setDeploymentName(activitiProperties.getDeploymentName());conf.setDatabaseSchema(activitiProperties.getDatabaseSchema());conf.setDatabaseSchemaUpdate(activitiProperties.getDatabaseSchemaUpdate());conf.setDbHistoryUsed(activitiProperties.isDbHistoryUsed());conf.setAsyncExecutorActivate(activitiProperties.isAsyncExecutorActivate());if (!activitiProperties.isAsyncExecutorActivate()) {ValidatorSet springBootStarterValidatorSet = new ValidatorSet("activiti-spring-boot-starter");springBootStarterValidatorSet.addValidator(new AsyncPropertyValidator());if (conf.getProcessValidator() == null) {ProcessValidatorImpl processValidator = new ProcessValidatorImpl();processValidator.addValidatorSet(springBootStarterValidatorSet);conf.setProcessValidator(processValidator);} else {conf.getProcessValidator().getValidatorSets().add(springBootStarterValidatorSet);}}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.isUseStrongUuids()) {conf.setIdGenerator(new StrongUuidGenerator());}if (activitiProperties.getDeploymentMode() != null) {conf.setDeploymentMode(activitiProperties.getDeploymentMode());}conf.setActivityBehaviorFactory(new CloudActivityBehaviorFactory());if (processEngineConfigurationConfigurer != null) {processEngineConfigurationConfigurer.configure(conf);}return conf;}@Bean@ConditionalOnMissingBeanpublic ProcessDefinitionResourceFinder processDefinitionResourceFinder(ActivitiProperties activitiProperties,ResourcePatternResolver resourcePatternResolver) {return new ProcessDefinitionResourceFinder(activitiProperties,resourcePatternResolver);}@Bean@ConditionalOnMissingBeanpublic ProcessDeployedEventProducer processDeployedEventProducer(RepositoryService repositoryService,APIProcessDefinitionConverter converter,@Autowired(required = false) List<ProcessRuntimeEventListener<ProcessDeployedEvent>> listeners) {return new ProcessDeployedEventProducer(repositoryService,converter,Optional.ofNullable(listeners).orElse(Collections.emptyList()));}
}
分析源码,梳理为以下三点:
  • ProcessEngineAutoConfiguration 被定义在 DataSourceAutoConfiguration 和 TaskExecutionAutoConfiguration 两个自动配置类启动后再启动。
  • ProcessEngineAutoConfiguration 通过 @EnableConfigurationProperties 启用 ActivitiProperties 的各项属性(application.yml 上的属性映射到了 ActivitiProperties 的属性中)。
  • 在 ProcessEngineAutoConfiguration 自动配置类内,注册了三个Bean,分别是:
    • SpringProcessEngineConfiguration:是 Activiti 中核心配置类 ProcessEngineConfigurationImpl 的子类,初始化 ProcessEngine 不可缺少的类。
    • ProcessDefinitionResourceFinder:用于查找 xxx.bpmn20.xml 流程定义资源文件
    • ProcessDeployedEventProducer:流程部署事件生产者,从现有代码看,侧重于测试,其它地方没有引用。

AbstractProcessEngineAutoConfiguration

ProcessEngineAutoConfiguration 的父类 AbstractProcessEngineAutoConfiguration 中注册了最核心的 Bean:ProcessEngineFactoryBean。ProcessEngineFactoryBean 这个 Bean 实现了 FactoryBean 接口,使得 Spring 可以通过该接口创建 Activiti 的心脏: ProcessEngine 流程引擎。除 ProcessEngine 外,还注册了我们常与流程引擎打交道用到的Bean,分别是:RuntimeService、RepositoryService、TaskService、HistoryService、ManagementService。

package org.activiti.spring.boot;import java.util.HashSet;
import java.util.List;
import java.util.Set;import org.activiti.engine.HistoryService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.persistence.entity.integration.IntegrationContextManager;
import org.activiti.engine.integration.IntegrationContextService;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.activiti.spring.SpringAsyncExecutor;
import org.activiti.spring.SpringCallerRunsRejectedJobsHandler;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.SpringRejectedJobsHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;/*** Provides sane definitions for the various beans required to be productive with Activiti in Spring.**/
public abstract class AbstractProcessEngineAutoConfigurationextends AbstractProcessEngineConfiguration {@Beanpublic SpringAsyncExecutor springAsyncExecutor(TaskExecutor applicationTaskExecutor) {return new SpringAsyncExecutor(applicationTaskExecutor, springRejectedJobsHandler());}@Bean public SpringRejectedJobsHandler springRejectedJobsHandler() {return new SpringCallerRunsRejectedJobsHandler();}protected Set<Class<?>> getCustomMybatisMapperClasses(List<String> customMyBatisMappers) {Set<Class<?>> mybatisMappers = new HashSet<>();for (String customMybatisMapperClassName : customMyBatisMappers) {try {Class customMybatisClass = Class.forName(customMybatisMapperClassName);mybatisMappers.add(customMybatisClass);} catch (ClassNotFoundException e) {throw new IllegalArgumentException("Class " + customMybatisMapperClassName + " has not been found.", e);}}return mybatisMappers;}@Beanpublic ProcessEngineFactoryBean processEngine(SpringProcessEngineConfiguration configuration) {return super.springProcessEngineBean(configuration);}@Bean@ConditionalOnMissingBean@Overridepublic RuntimeService runtimeServiceBean(ProcessEngine processEngine) {return super.runtimeServiceBean(processEngine);}@Bean@ConditionalOnMissingBean@Overridepublic RepositoryService repositoryServiceBean(ProcessEngine processEngine) {return super.repositoryServiceBean(processEngine);}@Bean@ConditionalOnMissingBean@Overridepublic TaskService taskServiceBean(ProcessEngine processEngine) {return super.taskServiceBean(processEngine);}@Bean@ConditionalOnMissingBean@Overridepublic HistoryService historyServiceBean(ProcessEngine processEngine) {return super.historyServiceBean(processEngine);}@Bean@ConditionalOnMissingBean@Overridepublic ManagementService managementServiceBeanBean(ProcessEngine processEngine) {return super.managementServiceBeanBean(processEngine);}@Bean@ConditionalOnMissingBeanpublic TaskExecutor taskExecutor() {return new SimpleAsyncTaskExecutor();}@Bean@ConditionalOnMissingBean@Overridepublic IntegrationContextManager integrationContextManagerBean(ProcessEngine processEngine) {return super.integrationContextManagerBean(processEngine);}@Bean@ConditionalOnMissingBean@Overridepublic IntegrationContextService integrationContextServiceBean(ProcessEngine processEngine) {return super.integrationContextServiceBean(processEngine);}
}

application.yml 配置

通过 @ConfigurationProperties("spring.activiti") 注解,将 application.yml 配置文件上的前缀是 spring.activiti 的属性映射到了 ActivitiProperties 类上。在这些属性中,processDefinitionLocationPrefix 配置 流程定义文件 的资源路径前缀,processDefinitionLocationSuffixes 配置 流程定义文件 的文件后缀。

package org.activiti.spring.boot;import java.util.Arrays;
import java.util.List;import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.activiti.engine.impl.history.HistoryLevel;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.support.ResourcePatternResolver;@ConfigurationProperties("spring.activiti")
public class ActivitiProperties {private boolean checkProcessDefinitions = true;private boolean asyncExecutorActivate = false;private String deploymentName = "SpringAutoDeployment";private String mailServerHost = "localhost";private int mailServerPort = 1025;private String mailServerUserName;private String mailServerPassword;private String mailServerDefaultFrom;private boolean mailServerUseSsl;private boolean mailServerUseTls;private String databaseSchemaUpdate = "true";private String databaseSchema;private boolean dbHistoryUsed = false;private HistoryLevel historyLevel = HistoryLevel.NONE;private String processDefinitionLocationPrefix = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/processes/";private List<String> processDefinitionLocationSuffixes = Arrays.asList("**.bpmn20.xml", "**.bpmn");private List<String> customMybatisMappers;private List<String> customMybatisXMLMappers;private boolean useStrongUuids = true;private boolean copyVariablesToLocalForTasks = true;private String deploymentMode = "default";private boolean serializePOJOsInVariablesToJson = true;private String javaClassFieldForJackson = JsonTypeInfo.Id.CLASS.getDefaultPropertyName();// 省略 getter 和 setter 
}

总结

Activiti 与 Spring Boot 整合,本质上就是把人为手动创建 ProcessEngine、RuntimeService、TaskService、RepositoryService、HistoryService 的行为,改为由 Spring 来帮忙创建。
得益于 Spring Boot 的约定优于配置高度模块化强大的扩展性等特点,使得 Activiti 与 Spring Boot 的整合变得简单,只需下面几步即可完成:
  1. 引入 spring-boot-autoconfigure 依赖包;
  2. 属性Propertie类上加上 @ConfigurationProperties 注解;
  3. 定义自动配置类,并加上 @EnableConfigurationProperties 和 @Configuration 注解;
  4. 在jar包上添加 META-INF/spring.factories 配置文件,把自动配置类配上。
http://www.dtcms.com/a/588806.html

相关文章:

  • 分布式专题——49 SpringBoot整合ElasticSearch8.x实战
  • 18_FastMCP 2.x 中文文档之FastMCP服务端高级功能:后端存储详解
  • 基于Spring Boot的社团服务系统的设计与实现
  • Spring Boot配置文件加载顺序详解(含Nacos配置中心机制)
  • 基于React+Flask前后端分离的文件搜索系统
  • K8s 集群部署中间件 - yaml 版本(二)
  • zmaiFy音频转录介绍
  • 学校资源网站建设目标关于做电商网站导流项目
  • 【论文阅读与项目复现】Hypothesis Generation with Large Language Models
  • win7下asp.net网站发布软件开发文档编写
  • socket编程——使用UDP实现的一个回显功能
  • 侠客行・iOS 26 Liquid Glass TabBar 破阵记
  • G882磁力仪方向调整
  • 站长友情链接网上卖货的平台有哪些
  • 弱函数:嵌入式回调的最佳实践
  • 如何在实验室服务器上搭建python虚拟环境?安装conda并配置虚拟环境
  • 【开发者导航】轻量可微调且开源的大语言模型家族:LLaMA
  • 北京网站建立公司创意包装设计网站
  • INSERT INTO … SELECT … 常见问答(含样例)
  • 做图素材的网站有哪些昆明做网站公司有哪些
  • 移动端网站定制搞笑网站模板
  • 网站后台的数据库怎么做工业产品设计要学什么
  • 你去湖北省住房城乡建设厅网站查软件开发好学吗
  • 北京手机网站设计公司益阳建设公司网站
  • 网站建设ip微信小程序 做网站
  • 单位网站建设的必要性网站如何被收录
  • 狗贩子怎么做网站卖狗成都网站建设餐饮
  • 如何开发网站自己做站长哪个网站可以做编程题
  • 深圳制作公司网站wordpress 显示微信二维码
  • 微信网站建设费记什么科目中山网站搭建