Activiti6.0工作流引擎学习(二) -- 引擎配置

Activiti6.0工作流引擎学习(二) – 引擎配置

1.Activiti流程引擎配置涉及的类

Activiti流程引擎配置

1.1 流程引擎配置类的作用

  1. ProcessEngineConfiguration
    • 查找并解析xml配置文件activiti.cfg.xml
    • 提供多个静态方法创建配置对象
      静态方法创建配置对象
    • 实现几个基于不同场景场景的子类,配置方式非常的灵活
      使用场景

1.2 部分子类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testConfig1(){
//通过默认来创建引擎;会通过spring的解析进行创建引擎对象
ProcessEngineConfiguration engine
= ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
log.info("engine ={}",engine);
}
@Test
public void testConfig2(){
//直接创建标准的引擎,直接new一个引擎对象
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
log.info("configuration = {}",configuration);
}

2.数据库配置

2.1 缺省配置默认,使用H2内存数据库

2.2 配置JDBC属性,使用mybatis提供的链接池

2.3 配置DataSource,可自选第三方实现

  1. Druid 为监控而生的数据库连接池 来自阿里

  2. Dbcp 老牌数据源连接池,稳定可靠,Tomcat自带
  3. HikariCP 来自日本的极速数据源连接池,Spring默选

2.4 数据库更新策略

  1. 配置databaseSchemaUpdate
    • false:启动时检查数据库版本,发生不匹配抛异常
    • true: 启动时自动检查并甭信数据库表,不存在会创建
    • create-drop: 启动时创建数据库表结构,结束时删除表结构

2.5 数据库配置编码

2.5.1 使用默认的配置文件创建流程引擎

activiti.cfg.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<!--<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />

&lt;!&ndash; Database configurations &ndash;&gt;
<property name="databaseSchemaUpdate" value="drop-create" />

<property name="history" value="full" />-->
</bean>

</beans>
1
2
3
4
5
6
7
8
public void test1(){
ProcessEngineConfiguration configuration =
ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
log.info("configuration = {}",configuration);
ProcessEngine processEngine = configuration.buildProcessEngine();
log.info("获取流程引擎 = [{}]",processEngine.getName());
processEngine.close();
}

2.5.2 使用druid数据源

activiti_druid.cfg.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

<!--<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />

&lt;!&ndash; Database configurations &ndash;&gt;-->
<property name="databaseSchemaUpdate" value="drop-create" />
<property name="dataSource" ref="dataSource"/>
<property name="history" value="full" />
<!--引擎是否使用历史数据-->
<property name="dbHistoryUsed" value="true"/>
<!--引擎是否使用身份验证-->
<property name="dbIdentityUsed" value="true"/>
</bean>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://111.229.203.5:3306/activiti?serverTimezone=CTT&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="weaver"/>
<property name="password" value="192612"/>
<property name="initialSize" value="1"/>
<property name="maxActive" value="10"/>
<property name="filters" value="stat,slf4j"/>
</bean>

</beans>
1
2
3
4
5
6
7
8
public void test2(){
ProcessEngineConfiguration configuration =
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti_druid.cfg.xml");
log.info("configuration = {}",configuration);
ProcessEngine processEngine = configuration.buildProcessEngine();
log.info("获取流程引擎 = [{}]",processEngine.getName());
processEngine.close();
}

3.日志记录配置

3.1 日志组件的关系及MDC

3.1.1 配置开启MDC(Mapped Diagnostic Contexts)

  1. 默认没有开启,需要手动设置LogMDC.setMDCEnable(true)
  2. 配置logback.xml日志模版 %X{mdcProcessInstanceID}
  3. 流程只有在执行过程出现异常才会记录MDC信息

3.2 配置历史记录级别(HistoryLevel)

3.2.1 配置HistoryLevel

  1. none:不记录历史流程,性能高,流程结束后不可读取
  2. activiti:归档流程实例和活动实例,流程变量不同步
  3. addit:默认值,在activiti基础上同步变量值,保存表单属性
  4. full:性能较差,记录所有实例和变量细节变化

3.3 配置基于db的事件日志(Event logging)

3.3.1 配置Event Logging

  1. 试验性的事件记录机制,性能影响较大
  2. 开启默认记录所有数据的变化过程,表记录快速增长
  3. 日志内容json格式,建议存入mongoDB、Elastic Search

3.4 日志记录 mdc

4 历史记录配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.atguigu.test;

import com.google.common.collect.Maps;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
* @program: activiti6
* @description: 测试MDC
* @author: Mr.Wang
* @create: 2020-14-31
*/
public class TestMDC {
private static Logger log = LoggerFactory.getLogger(TestMDC.class);
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
@Test
//定义流程文件部署
@Deployment(resources = {"my-process.bpmn20.xml"})
public void test(){
Map<String,Object> params = Maps.newHashMap();
params.put("keyStart1","value1");
params.put("keyStart2","value2");
//启动流程
ProcessInstance processInstance =
activitiRule.getRuntimeService().startProcessInstanceByKey("my-process",params);
//修改变量
List<Execution> executions = activitiRule.getRuntimeService().createExecutionQuery().listPage(0, 100);
for (Execution execution : executions) {
log.info("executions {}",executions);
}
log.info("executions size = {}",executions.size());
String id = executions.iterator().next().getId();
activitiRule.getRuntimeService().setVariable(id,"keyStart1","value1_");
//提交表单task 修改变量
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
Map<String,String> properties = Maps.newHashMap();
properties.put("formkey1","valuef1");
properties.put("formkey2","valuef2");
activitiRule.getFormService().submitTaskFormData(task.getId(),properties);
//activitiRule.getTaskService().complete(task.getId());
//输出历史活动
List<HistoricActivityInstance> historicActivityInstances = activitiRule.getHistoryService().createHistoricActivityInstanceQuery().listPage(0, 100);
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
log.info("historicActivityInstances = {}",historicActivityInstances);
}
log.info("historicActivityInstances size = {}",historicActivityInstances.size());

//输出历史表单

//输出历史详情
List<HistoricDetail> historicDetails = activitiRule.getHistoryService().createHistoricDetailQuery().listPage(0, 100);
for (HistoricDetail historicDetail : historicDetails) {
log.info("historicDetail [{}]",historicDetail);
}
log.info("historicDetail size = [{}]", historicDetails.size());
}
}

5.事件处理及监听配置-eventlog

5.1 事件及监听器原理

5.2 监听器的配置方式

  1. 配置Listener
    1. eventListeners:监听所有事件派发通知
    2. typedEventListeners:监听指定事件类型的通知
    3. activiti:eventListener:只监听特定流程定义的事件

5.3 Activiti事件监听

5.3.1 相关API

  • ActivitiEvent:事件对象
  • ActivitiEventListener:监听器
  • ActivitiEventType:事件类型

5.4 ActivitiEventListener监听器的使用

  1. 配置文件中增加对应的配置
1
2
3
4
5
6
7
<!--配置事件监听器-->
<property name="eventListeners">
<list>
<!--监听器的实现类-->
<bean class="com.atguigu.event.ProcessEventListener"/>
</list>
</property>
  1. 创建一个实体类,实现ActivitiEventListener
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.atguigu.event;

import org.activiti.bpmn.model.ActivitiListener;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @program: activiti6
* @description: 流程监听
* @author: Mr.Wang
* @create: 2020-05-27-21-26
*/
public class ProcessEventListener implements ActivitiEventListener {
private static Logger log = LoggerFactory.getLogger(ProcessEventListener.class);
@Override
public void onEvent(ActivitiEvent event) {
ActivitiEventType type = event.getType();
//判断是是否为流程启动
if(ActivitiEventType.PROCESS_STARTED.equals(type)){
log.info("流程启动 {}",event.getProcessInstanceId());
}else if(ActivitiEventType.PROCESS_COMPLETED.equals(type)){
log.info("流程结束");
}
}

@Override
public boolean isFailOnException() {
return false;
}
}

5.5 TypedEventListeners监听器的使用

  1. 配置文件
1
2
3
4
5
6
7
8
9
10
<property name="typedEventListeners">
<map>
<!--这样配置的时候,只会监听流程启动-->
<entry key="PROCESS_STARTED">
<list>
<bean class="com.atguigu.event.ProcessEventListener"/>
</list>
</entry>
</map>
</property>
  1. 流程监听实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.atguigu.event;

import org.activiti.bpmn.model.ActivitiListener;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @program: activiti6
* @description: 流程监听
* @author: Mr.Wang
* @create: 2020-05-27-21-26
*/
public class ProcessEventListener implements ActivitiEventListener {
private static Logger log = LoggerFactory.getLogger(ProcessEventListener.class);
@Override
public void onEvent(ActivitiEvent event) {
ActivitiEventType type = event.getType();
//判断是是否为流程启动
if(ActivitiEventType.PROCESS_STARTED.equals(type)){
log.info("流程启动 {}",event.getProcessInstanceId());
}else if(ActivitiEventType.PROCESS_COMPLETED.equals(type)){
log.info("流程结束");
}
}

@Override
public boolean isFailOnException() {
return false;
}
}
//输出的结果为:22:18:38.450 [main] [INFO ]流程启动 4 c.a.e.ProcessEventListener.onEvent:23

6. 命令拦截器配置–command

6.1 命令模式与责任链模式

6.1.1 命令模式


时序图

6.1.2 责任链模式

  • CommandInterceptor
  • 配置Interceptor
    • customPreCommandInterceptors:配置在默认拦截器之前
    • customPostCommandInterceptors:配置在默认拦截器之后
    • commandInvoker:配置最后的执行器

6.2 拦截器的配置方式

  1. activiti.cfg.xml文件的修改
1
2
3
4
5
6
<!--拦截器的配置-->
<property name="customPreCommandInterceptors">
<list>
<bean class="com.atguigu.interceptor.DurationInterceptor"/>
</list>
</property>
  1. 实体类的编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.atguigu.interceptor;

import org.activiti.engine.impl.interceptor.AbstractCommandInterceptor;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandConfig;

/**
* @program: activiti6
* @description: 执行的时间
* @author: Mr.Wang
* @create: 2020-05-27-22-53
*/
public class DurationInterceptor extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
long l = System.currentTimeMillis();
this.getNext().execute(config,command);
long l1 = System.currentTimeMillis();
long duration = l1 - l;
System.out.println("执行的时长----->"+duration);
return null;
}
}

6.3 Activiti的拦截器

7. 作业执行器 Job Executor

7.1 作业执行器配置

(1) asyncExecutorActivate:激活作业执行器
(2) asyncExectuorXXX:异步执行器的属性配置
(3) asyncExecutor:异步执行器的bean

7.2 配置自定义线程池

7.2.1 自定义线程池ExecutorService

(1) corePoolSize:核心线程数
(2) maxPoolSize: 最大线程数
(3) queueCapacity:堵塞队列大小

7.2.2 基于spring线程池的配置

7.3 流程定义定时启动流程

7.3.1 定时开始事件

  1. timeDate:指定启动时间
  2. timeDuration:指定持续时间间隔后执行
  3. timeCycle:R5/P1DT1H指定事件段后周期执行

7.4 配置文件修改

activiti.cfg.xml文件修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!--打开定时任务的激活器-->
<property name="asyncExecutorActivate" value="true"/>
<!--定义异步执行器-->
<property name="asyncExecutor" ref="defaultAsyncJobExecutor"/>
</bean>
<!--系统默认的自定义任务执行器-->
<bean id = "defaultAsyncJobExecutor" class="org.activiti.engine.impl.asyncexecutor.DefaultAsyncJobExecutor">
<!--需要的服务-->
<property name="executorService" ref="executorService"/>
</bean>
<!--使用spring的线程池-->
<bean id = "executorService" class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
<!--具体的配置-->
</bean>

8 Activiti与Spring的集成

8.1 集成Spring配置

8.1.1 相关配置

(1) 添加pom依赖activiti-spring

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>6.0.0</version>
</dependency>

(2) 基于Spring的默认配置activiti-context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--数据源配置-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://111.229.203.5:3306/activiti?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="weaver"/>
<property name="password" value="192612"/>
<property name="initialSize" value="1"/>
<property name="maxActive" value="10"/>
<property name="filters" value="stat,slf4j"/>
</bean>
<!--事务管理器配置-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="processEngineConfiguration"
class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<!--配置事务管理-->
<property name="transactionManager" ref="dataSourceTransactionManager"/>
<!--数据源的创建格式-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
<!--构造流程引擎对象-->
<bean id = "processEngineFactoryBean" class="org.activiti.spring.ProcessEngineFactoryBean">
<!--指定流程配置对象-->
<property name="processEngineConfiguration" ref="processEngineConfiguration"/>
</bean>
<!--报漏服务给Spring-->
<bean id="runtimeService" factory-bean="processEngineFactoryBean" factory-method="getRuntimeService"></bean>
<bean id="repositoryService" factory-bean="processEngineFactoryBean" factory-method="getRepositoryService"></bean>
<bean id="formService" factory-bean="processEngineFactoryBean" factory-method="getFormService"></bean>
<bean id="taskService" factory-bean="processEngineFactoryBean" factory-method="getTaskService"></bean>
<bean id="historyService" factory-bean="processEngineFactoryBean" factory-method="getHistoryService"></bean>
<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
<property name="processEngine" ref="processEngineFactoryBean"/>
</bean>
</beans>

(3) Activiti核心服务注入Spring容器
详细配置见上一步

8.2 基于Spring对Activiti管理

8.2.1 功能特征

(1) 集成Spring事务管理器
(2) 定义文件表达式中使用Spring bean
(3) 自动部署资源文件

8.3 基于Spring的流程单元测试

8.3.1 单元测试

(1) 添加pom依赖spring-test

1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
<scope>test</scope>
</dependency>

(2) 辅助测试Rule:ActivitiRule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.atguigu.test;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
* @program: activiti6
* @description: 测试与Spring的集成
* @author: Mr.Wang
* @create: 2020-05-28-10-26
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:activiti-context.xml"})
public class TestSpring {
private static Logger log = LoggerFactory.getLogger(TestSpring.class);

@Rule
@Autowired
public ActivitiRule activitiRule;

@Autowired
private RuntimeService runtimeService;

@Autowired
private TaskService taskService;
@Test
@Deployment(resources = {"my-process.bpmn20.xml"})
public void test(){
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("my-process");
/*List<Task> tasks = taskService.createTaskQuery().listPage(0, 100);
for (Task task : tasks) {
log.info("task 的某些测试数据 {}",task);
}*/
Task task = taskService.createTaskQuery().singleResult();
taskService.complete(task.getId());
}


}

(3) 辅助测试TestCase:SpringActivitiTestCase