系列文章目录
前言
学习activiti记录过程中的一些问题。
springBoot 版本 2.5.2
activiti版本 7.1.0.M6
一、Demo
1.1 项目整体架构
因为这是一个学习记录的过程,所以可能会有多个demo,项目结构如下:
1.2 父工程
1.2.1 POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.chzu</groupId>
<artifactId>activiti</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>activiti_example_01</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<!-- 各个依赖的版本号 -->
<activiti.version>7.1.0.M6</activiti.version>
<mysql.version>8.0.18</mysql.version>
<jaskson-datebind.version>2.10.2</jaskson-datebind.version>
<!-- 设置编码为UTF-8 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jaskson-datebind.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
1.3 子工程(activiti_example_01)
1.3.1 POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>activiti</artifactId>
<groupId>org.chzu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>activiti_example_01</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
1.3.2 YML
spring:
##数据库连接信息
datasource:
# 数据源配置
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/activiti?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: root
activiti:
#1.false:默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
#2.true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
#3.create_drop:启动时自动创建表,关闭时自动删除表
#4.drop_create:启动时,删除旧表,再创建新表
database-schema-update: true
#activiti7默认不生成历史信息表,需手动设置开启
db-history-used: true
#full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
history-level: full
#自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: true
process-definition-location-prefix: classpath:/processes/
process-definition-location-suffixes:
- "**.png"
- "**.bpmn20.xml"
1、配置数据库信息
2、配置activiti
check-process-definitions
process-definition-location-prefix是指定activiti流程描述文件的即路径,启动时,activiti就会去寻找此路径下的流程描述文件,并且自动部署;
process-definition-location-suffixes是一个String数组,表示描述文件的默认后缀名;
默认配置如下:
@ConfigurationProperties("spring.activiti")
class ActivitiProperties
......
public ActivitiProperties() {
this.historyLevel = HistoryLevel.NONE;
this.processDefinitionLocationPrefix = "classpath*:**/processes/";
this.processDefinitionLocationSuffixes = Arrays.asList("**.bpmn20.xml", "**.bpmn");
this.useStrongUuids = true;
this.copyVariablesToLocalForTasks = true;
this.deploymentMode = "default";
this.serializePOJOsInVariablesToJson = true;
this.javaClassFieldForJackson = Id.CLASS.getDefaultPropertyName();
}
也可以使用手动部署
1.3.3 创建流程定义文件
2020版idea搜索bpmn插件,结果如图所示:
我下载了如图的两个插件。
在resources文件夹下创建processes文件夹(如果没有此文件夹,启动会报错,大致是找不到processes文件夹),在processes文件夹下创建如图所示文件:
点击如下图所示的Designer,就可以定义了,连线直接点击想要连接的两个图标就行了。
但是使用这个插件遇到的(有可能是我自己的问题):
1、只有第一次拖拽的时候可以移动位置,确定后无法调整
2、没法在图标上直接修改名称(上图中文是在xml文件中修改)
3、它这里的Task只有Service Task一个选项(上图也是在xml中将Service Task 修改为User Task 的)
右键选择如图所示,将其保存为png图片,其实也可以在Designer保存,但是Designer无法调整图标位置
1.3.4 完整的的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/processdef">
<process id="holiday" name="holiday" isExecutable="true">
<endEvent id="EndEvent_1" name="End Event 1"/>
<sequenceFlow sourceRef="StartEvent_1" targetRef="ServiceTask_1" id="StartEvent_1-ServiceTask_1"/>
<sequenceFlow sourceRef="ServiceTask_1" targetRef="ServiceTask_2" id="ServiceTask_1-ServiceTask_2"/>
<sequenceFlow sourceRef="ServiceTask_2" targetRef="EndEvent_1" id="ServiceTask_2-EndEvent_1"/>
<userTask id="ServiceTask_1" name="填写请假申请单" activiti:assignee="zhangsan"/>
<userTask id="ServiceTask_2" name="管理员审批" activiti:assignee="lisi"/>
<startEvent id="StartEvent_1" name="Start Event 1"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_holiday">
<bpmndi:BPMNPlane bpmnElement="holiday" id="BPMNPlane_holiday">
<bpmndi:BPMNShape bpmnElement="StartEvent_1">
<omgdc:Bounds height="48.0" width="48.0" x="619.0" y="96.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="ServiceTask_1">
<omgdc:Bounds height="48.0" width="120.0" x="655.0" y="225.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="ServiceTask_2">
<omgdc:Bounds height="48.0" width="120.0" x="631.0" y="360.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="EndEvent_1">
<omgdc:Bounds height="48.0" width="48.0" x="667.0" y="454.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="StartEvent_1-ServiceTask_1">
<omgdi:waypoint x="643.0" y="120.0"/>
<omgdi:waypoint x="715.0" y="249.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="ServiceTask_1-ServiceTask_2">
<omgdi:waypoint x="715.0" y="249.0"/>
<omgdi:waypoint x="691.0" y="384.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="ServiceTask_2-EndEvent_1">
<omgdi:waypoint x="691.0" y="384.0"/>
<omgdi:waypoint x="691.0" y="478.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
二、测试
可以设置日志的级别为DEBUG,这样可以在运行栏中看到操作了哪些数据库表
创建一个测试类
@SpringBootTest(classes = Example01.class)
public class ActivitiTest {
@Autowired
private SecurityUtil securityUtil;
@Autowired
RepositoryService repositoryService;
@Autowired
RuntimeService runtimeService;
@Autowired
TaskService taskService;
}
2.1 手动部署流程
@Test
public void deploy(){
//定义流程名称,并把bpmn与png文件部署到数据库
DeploymentBuilder builder = repositoryService.createDeployment();
Deployment deployment = builder
.name("请假申请流程")
.addClasspathResource("processes/holiday.bpmn20.xml")
.addClasspathResource("processes/holiday.bpmn20.png")
.deploy();
//输出部署信息
System.out.println("流程ID"+deployment.getId());
System.out.println("流程名称"+deployment.getName());
System.out.println("流程部署时间"+deployment.getDeploymentTime());
}
如果你的配置文件中设置了自动部署,在这里又手动部署的话,其实是没什么影响的,数据库中的记录有一个VERSION字段,是用来区别的同一个流程文件版本的。
当存在多个流程文件时也可以将流程文件打包部署
InputStream in = this.getClass().getClassLoader().getResourceAsStream("processes/holiday.zip");
ZipInputStream stream = new ZipInputStream(in);
Deployment deploy = repositoryService.createDeployment()
.addZipInputStream(stream)
.deploy();
2.2 启动流程实例
//启动流程实例,根据流程的id holiday 获取流程实例
//<process id="holiday" name="请假流程" isExecutable="true">
ProcessInstance instance = runtimeService.startProcessInstanceByKey("holiday");
String id = instance.getId();
String name = instance.getName();
Map<String, Object> variables = instance.getProcessVariables();
String businessKey = instance.getBusinessKey();
String definitionId = instance.getProcessDefinitionId();
String activityId = instance.getActivityId();
//可以输出一些信息
System.out.println("流程定义的ID :"+definitionId);
System.out.println("name"+name);
2.3 查询个人任务
//参与者查询自己的任务
//根据key和任务负责人查询任务,返回一个Task列表
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("holiday")
.taskAssignee("zhangsan")
.list();
//输出
for (Task task : list) {
log.info(task.getId());
log.info(task.getName());
}
2.4 查询和完成任务
//参与者查询自己的任务,这里singleResult()返回一个Task实例
Task task1 = taskService.createTaskQuery()
.processDefinitionKey("holiday")
.taskAssignee("lisi")
.singleResult();
//完成任务
taskService.complete(task1.getId());
2.5 下载流程定义文件
//查询流程的定义信息
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("holiday")
.singleResult();
//通过流程定义信息,获取部署ID
String deploymentId = definition.getDeploymentId();
//通过部署ID读取资源
String resourceName = definition.getResourceName();
InputStream stream1 = repositoryService.getResourceAsStream(deploymentId, resourceName);
File file1 = new File("1.bpmn");
FileOutputStream out1 = new FileOutputStream(file1);
byte[] bytes = new byte[1024];
BufferedInputStream bis1 = new BufferedInputStream(stream1);
BufferedOutputStream bos1 = new BufferedOutputStream(out1);
while ((len = bis1.read(bytes)) != -1){
bos1.write(bytes,0,len);
}
bis1.close();
bos1.close();
2.6 流程的删除
//删除流程时若流程已经启动则删除实例会报错
repositoryService.deleteDeployment("ac9105ee-1848-11ec-a8c9-005056c00001");
//即使流程已经启动,也会删除
//repositoryService.deleteDeployment("ac9105ee-1848-11ec-a8c9-005056c00001",true);
三、总结
3.1 springboot对Activiti自动配置
ProcessEngineAutoConfiguration 注册一个springProcessEngineConfiguration组件
public class ProcessEngineAutoConfiguration extends AbstractProcessEngineAutoConfiguration{
@Bean
@ConditionalOnMissingBean
public SpringProcessEngineConfiguration springProcessEngineConfiguration(....){
}
}
SpringProcessEngineConfiguration 继承了ProcessEngineConfigurationImpl 并重写了(调用了父类的方法)buildProcessEngine()方法,这个方法返回一个ProcessEngine,它是activiti流程引擎核心类。
public class SpringProcessEngineConfiguration extends ProcessEngineConfigurationImpl implements ApplicationContextAware {
......
@Override
public ProcessEngine buildProcessEngine() {
ProcessEngine processEngine = super.buildProcessEngine();
ProcessEngines.setInitialized(true);
autoDeployResources(processEngine);
return processEngine;
}
......
ProcessEngineConfigurationImpl 的buildProcessEngine()方法,这个方法中调用的init()方法初始化很多的资源,比如其中的xxxxService。
public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfiguration {
.....
protected RepositoryService repositoryService = new RepositoryServiceImpl();
protected RuntimeService runtimeService = new RuntimeServiceImpl();
protected HistoryService historyService = new HistoryServiceImpl(this);
protected TaskService taskService = new TaskServiceImpl(this);
protected ManagementService managementService = new ManagementServiceImpl();
......
public ProcessEngine buildProcessEngine() {
this.init();
ProcessEngineImpl processEngine = new ProcessEngineImpl(this);
this.postProcessEngineInitialisation();
return processEngine;
}
public void init() {
.....
initServices
.....
}
public void initServices() {
i nitService(repositoryService);
initService(runtimeService);
initService(historyService);
initService(taskService);
initService(managementService);
initService(dynamicBpmnService);
}
}
3.2 遇到的问题
在下载上传的流程定义png资源的时候,在数据库的act_re_procdef表中【DGRM_RESOURCE_NAME_】为png流程图片名称。在我上传流程定义资源之后表act_ge_bytearray中存在png文件,但是在act_re_procdef中【DGRM_RESOURCE_NAME_】为null。
我没有找到原因,希望有知道原因的同学告知一下
问题解决:发现是自己的图片保存的名称是holiday.bpmn20.png,名称后缀一定要是**.png才行,修改名称为holiday.png**,就可以了