Activiti6.0学习实践(4)-流程引擎配置一
在上一节,我们进行了一个hello world 的简单应用搭建,本节继续对activiti的一些重要组件进行更进一步的分析。
目录
2.1 ProcessEngineConifguration:
2.2 activiti.cfg.xml文件的配置
1、activiti工程骨架
activiti源码包里面,提供了一份工程骨架,可以将它安装到本地的仓库中
上图中tooling目录下的archetypes目录下就是源码提供的骨架,你可以自己在此基础上进行修改。
1.2 添加demo类到骨架工程
我们把上一节写的demomain.java 放到这里面,注意类的包名是特殊写法,用来创建骨架工程,首先是整体结构如下图:
接着注意DemoMain.java中包名的写法:
下一步要修改打包的骨架元数据,将这里 src/test/java 修改为 src,表示src目录下的所有java类都将被打包进去。修改其他的配置参数的TRUE和false,完成这一步后就可以进行骨架包的打包工作了。
然后,我们进入命令行,进入tooling\archetypes目录,执行 mvn clean install, 这个命令执行成功以后,就把骨架库安装到本地的maven库中。
1.3 创建基于骨架的maven工程
接着我们创建一个maven工程,在选择骨架的时候,要进行添加(将库中的包的GAV id)添加后,就可以创建了。
生成的工程如下所示:
2、流程引擎
流程引擎是actitivi中最重要的组件,作为核心,它的构造过程如下图所示:
2.1 ProcessEngineConifguration:
查找配置文件并解析activiti.cfg.xml
可以根据不同的场景进行灵活的配置,具体有如下7个创建引擎配置对象的方法
使用(ctrl+H)可以查看下图显示的类的继承图谱
这里重要的类是ProcessEngineConfigurationImpl这个抽象类(名字以impl结尾,但它是一个抽象类),这个类的属性基本上是引擎配置对象最主要的属性
public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfiguration
这个抽象类有4000多行是ProcessEngineConfiguration的主要实现。
在这个抽象类的构造方法中,我们看到默认使用的基于内存H2的数据库,这也是为什么不指定流程引擎的配置文件,我们依然可以正常启动并进行操作的原因。下图中显示了构造函数的部分内容。
2.2 activiti.cfg.xml文件的配置
下面来看一下activiti.cfg.xml,这个是流程引擎配置对象的配置文件,当然没有这个配置文件也可以启动一个默认配置的对象(前面说了抽象类中默认指定了配置为h2的内存库,解决了启动时的数据库依赖问题)
<?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="databaseType" value="mysql"></property> <!-- 数据库类型,最好配置一下 -->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti6?characterEncoding=UTF-8"></property> <!-- 数据库URL,我放在名为activiti数据库中 -->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property> <!-- 数据库驱动类 mysql是这个,其它的数据库修改一下即可 -->
<property name="jdbcUsername" value="root"></property> <!-- 连接数据库的账号 -->
<property name="jdbcPassword" value="root123"></property> <!-- 连接数据库的密码 -->
<property name="databaseSchema" value="activiti"></property> <!--activiti这个属性可以进行库表创建 true , create-drop -->
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
这里几个说明:
- processEngineConfiguration的实现类有多种实现,可以根据具体的场景进行选用,本例中采用的是StandaloneProcessEngineConfiguration实现。
- 数据库jdbcDriver:可以支持多种数据库
- databaseSchemaUpdate 配置项,是告诉系统初始情况下,如果没有创建库表,那么会创建系统的库表。
3、数据库配置
流程引擎默认是使用H2内存数据库,即便什么都不配置,也能正常启动,但是进行的一些修改不能保存下来,重启工程后,修改内容不会保存下来。
数据库配置项目主要有:
基础配置:jdbcUrl,jdbcDriver,jdbcUsername,jdbcPassword,
连接池相关配置:jdbcMaxActiveConnections, jdbcMaxIdleConnections, jdbcMaxCheckoutTime, jdbcMaxWaitTime
3.1 支持的数据库
支持的数据库种类比较全,有h2, mysql,oracle,postgres,db2, mssql
3.2 数据源配置:
主要的第三方实现的数据源DataSource:
Druid(阿里),Dbcp(Tomcat自带), HikariCP(光, Spring默认)
3.3 数据库更新策略
DatabaseSchemaUpdate
False:启动检查数据库版本,发生不匹配抛异常(生产环境时配置)
True:启动检查并更新数据库表,不存在会创建(一般在开发环境配置如此)
Create-drop:启动时创建数据库库表结构,结束时删除(试用于测试)
3.4 采用默认H2数据库
下面我们采用默认的H2数据库,具体配置如下:
Pom.xml 中添加h2数据库依赖
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>test</scope>
</dependency>
配置activiti的配置文件 activiti.cfg.xml
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
</bean>
我们看一下执行日志
3.5 采用mysql数据库
下面是采用mysql数据库的配置内容
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="databaseType" value="mysql"></property> <!-- 数据库类型,最好配置一下 -->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti6?characterEncoding=UTF-8"></property> <!-- 数据库URL,我放在名为activiti数据库中 -->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property> <!-- 数据库驱动类 mysql是这个,其它的数据库修改一下即可 -->
<property name="jdbcUsername" value="root"></property> <!-- 连接数据库的账号 -->
<property name="jdbcPassword" value="root123"></property> <!-- 连接数据库的密码 -->
<property name="databaseSchemaUpdate" value="true"></property> <!--activiti这个属性可以进行库表创建 true , create-drop -->
</bean>
执行结果
3.6 采用datasource方式配置
Activiti.cfg.xml 配置内容
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"></property> <!-- dataSource bean -->
<property name="databaseSchemaUpdate" value="true"></property> <!--activiti这个属性可以进行库表创建 true , create-drop -->
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <!-- 数据库驱动类 mysql是这个,其它的数据库修改一下即可 -->
<property name="url" value="jdbc:mysql://localhost:3306/activiti6?characterEncoding=UTF-8"></property> <!-- 数据库URL,我放在名为activiti数据库中 -->
<property name="username" value="root"></property> <!-- 连接数据库的账号 -->
<property name="password" value="root123"></property> <!-- 连接数据库的密码 -->
<property name="initialSize" value="1"></property> <!-- -->
<property name="maxActive" value="20"></property> <!-- -->
<property name="filters" value="stat,slf4j"></property> <!-- -->
</bean>
采用dataSource方式的执行结果
说明:在ProcessEngineConfigurationImpl中有初始化dataSource的过程,如下图所示
4、日志和数据记录
4.1、activiti的日志组件
常用日志组件可以用如下图表示
在activiti中通常使用slf4j, logbackde 方式。对于日志的配置需要了解一下两点,在logback.xml中配置日志模板%x{mdcProcessInstanceID},引擎的设计是在执行过程中出现异常才会记录MDC(Mapped Diagnostic Contexts)信息。配置流程历史记录级别HistoryLevel有4个级别,不同的级别记录的细节不一样,从低到高分为none,activiti,aduit,full。从aduit开始保存表单属性,同时也说明这个级别开始对性能影响比较显著。下面代码演示了如何开启MDC记录流程日志
4.2 测试代码
ConfigMDCTest.java
这个类是进行流程操作的测试类,我们期望这个流程执行过程中打印一些流程执行的过程。
import org.activiti.engine.logging.LogMDC;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Describe:
*
* @author cwqsolo
* @date 2020/01/07
*/
public class ConfigMDCTest {
private static final Logger logger = LoggerFactory.getLogger(ConfigMDCTest.class);
//这里已经包含了流程引擎的创建
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
@Test
@Deployment( resources = {"./my-process.bpmn20.xml"})
public void configMDCTest1(){
//LogMDC.setMDCEnabled(true);
ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");;
assertNotNull(processInstance);
List<Task> list = activitiRule.getTaskService().createTaskQuery().list();
// assertEquals("Activiti is awesome!", list.get(0).getName());
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
assertEquals("Activiti is awesome!", task.getName());
activitiRule.getTaskService().complete( list.get(0).getId() );
}
}
由于流程引擎设计时只是将错误发生时打印流程信息如id,name,因此在我们需要跟踪流程时打印的信息不够方便,因此需要构造一个拦截器,将拦截器设置到流程中,这样流程运行时,可以通过拦截器打印日志,activiti提供的打印对象为LogMDC。我们实现的拦截器MDCCommandInvoke.java代码如下:
package com.study.activiti.interceptor;
import org.activiti.engine.impl.agenda.AbstractOperation;
import org.activiti.engine.impl.interceptor.DebugCommandInvoker;
import org.activiti.engine.logging.LogMDC;
/**
* Describe:
* 设置一个MDC拦截器,可以在非error状态下,就可以打印流程的id等
* @author cwqsolo
* @date 2020/01/08
*/
public class MDCCommandInvoke extends DebugCommandInvoker {
@Override
public void executeOperation(Runnable runnable) {
//首先判断一个是否已经设置mdc生效
boolean mdcEnabled = LogMDC.isMDCEnabled();
LogMDC.setMDCEnabled(true);
if( runnable instanceof AbstractOperation){
AbstractOperation operation = (AbstractOperation) runnable;
if( operation.getExecution() != null){
LogMDC.putMDCExecution( operation.getExecution());
}
}
super.executeOperation(runnable);
//进行logMDC清理,如果原来已经设置mdc enable,则恢复设置
LogMDC.clear();
if(!mdcEnabled){
LogMDC.setMDCEnabled( false);
}
}
}
4.3 配置文件
前面显示的测试代码,要让拦截器工作起来,还需要在配置文件中设定这个拦截器,具体配置文件activiti.cfg.xml的内容如下:
<?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.StandaloneInMemProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"></property> <!-- dataSource bean -->
<property name="databaseSchemaUpdate"
value="false"></property> <!--activiti这个属性可以进行库表创建 true, false , create-drop -->
<property name="commandInvoker" ref="commandInvoker"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property> <!-- 数据库驱动类 mysql是这个,其它的数据库修改一下即可 -->
<property name="url"
value="jdbc:mysql://localhost:3306/activiti6?characterEncoding=UTF-8"></property> <!-- 数据库URL,我放在名为activiti数据库中 -->
<property name="username" value="root"></property> <!-- 连接数据库的账号 -->
<property name="password" value="root123"></property> <!-- 连接数据库的密码 -->
<property name="initialSize" value="1"></property> <!-- -->
<property name="maxActive" value="20"></property> <!-- -->
<property name="filters" value="stat,slf4j"></property> <!-- -->
</bean>
<!--将我们自定义的流程引擎打印信息用的拦截器设置到流程引擎里面 -->
<bean id="commandInvoker" class="com.study.activiti.interceptor.MDCCommandInvoke" />
</beans>
5、历史记录配置
Activiti提供了历史记录配置功能,结合配置文件中的history属性可以进行历史记录的不同详细粒度的查询
5.1、历史记录的使用
下面的代码展示了如何进行历史记录的查询使用:
ConfigHistoryLevelTest.java 里面已经进行了注释说明如何获取历史流程对象、历史流程任务、历史流程活动、历史流程详情、历史流程表单。
import com.google.common.collect.Maps;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.logging.LogMDC;
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.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Describe:
*
* @author cwqsolo
* @date 2020/01/08
*/
public class ConfigHistoryLevelTest {
private static final Logger logger = LoggerFactory.getLogger(ConfigHistoryLevelTest.class);
//这里已经包含了流程引擎的创建
@Rule
public ActivitiRule activitiRule = new ActivitiRule("activiti_his.cfg.xml");
@Test
@Deployment( resources = {"./my-process.bpmn20.xml"})
public void test(){
//定义参数
HashMap<String, Object> params = Maps.newHashMap();
params.put("key1","value1");
params.put("key2", "value2");
//启动流程
ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", params);
//修改变量方式,从执行对象获取到变量(也就是流程数据),可以对流程数据进行修改
List<Execution> executions = activitiRule.getRuntimeService().createExecutionQuery()
.listPage(0, 100);
logger.info(" ++++++++++输出流程对象+++++++++++");
for (Execution execution:executions ) {
logger.info("execution = {} ", execution);
}
logger.info("execution.size ={}", executions.size());
String id = executions.iterator().next().getId(); //取出id
activitiRule.getRuntimeService().setVariable( id, "key1", "value1_1");
//通过表单进行数据的修改
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
Map<String, String> properties=Maps.newHashMap();
properties.put("fromkey1", "valuef1");
properties.put("formkey2", "valuef2");
activitiRule.getFormService().submitTaskFormData( task.getId(), properties);
//输出历史内容
//1.输出历史活动
List<HistoricActivityInstance> historicActivityInstances = activitiRule.getHistoryService()
.createHistoricActivityInstanceQuery().listPage(0, 100);
logger.info(" ++++++++++输出流程活动历史+++++++++++");
for (HistoricActivityInstance historicActivityInstance: historicActivityInstances ) {
logger.info("histroricActivityInstance = {}", historicActivityInstance);
}
logger.info("historicActivityInstances.size = {}", historicActivityInstances.size());
//2.输出历史任务
List<HistoricTaskInstance> historicTaskInstances = activitiRule.getHistoryService()
.createHistoricTaskInstanceQuery().listPage(0, 100);
logger.info(" ++++++++++输出流程任务历史+++++++++++");
for (HistoricTaskInstance historicTaskInstance: historicTaskInstances ) {
logger.info("histroricTaskInstance = {}", historicTaskInstance);
}
logger.info("historicTasknstances.size = {}", historicTaskInstances.size());
//输出历史变量
List<HistoricVariableInstance> historicVariableInstances = activitiRule.getHistoryService()
.createHistoricVariableInstanceQuery().listPage(0, 100);
logger.info(" ++++++++++输出流程历史变量+++++++++++");
for (HistoricVariableInstance historicVariableInstance: historicVariableInstances ) {
logger.info("historicVariableInstance = {}", historicVariableInstance);
}
logger.info("historicVariableInstances.size = {}", historicVariableInstances.size());
//3.输出历史表单
List<HistoricDetail> historicDetailsForms = activitiRule.getHistoryService().createHistoricDetailQuery()
.formProperties().listPage(0, 100);
logger.info(" ++++++++++输出流程历史表单+++++++++++");
for (HistoricDetail historicDetailsForm: historicDetailsForms ) {
logger.info("historicDetailsForm = {}",to_String( historicDetailsForm));
}
logger.info("historicDetailsForms.size = {}", historicDetailsForms.size());
//4.输出历史详情
List<HistoricDetail> historicDetails = activitiRule.getHistoryService().createHistoricDetailQuery()
.listPage(0, 100);
logger.info(" ++++++++++输出流程历史详情+++++++++++");
for (HistoricDetail historicDetail: historicDetails ) {
logger.info("historicDetailsForm = {}", to_String(historicDetail));
}
logger.info("historicDetailsForms.size = {}", historicDetails.size());
}
/**
* 使用对象转换为字符串的方法,使得对象里面的数据看起来更清晰
*/
static String to_String(HistoricDetail historicDetail){
return ToStringBuilder.reflectionToString(historicDetail, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
5.2 历史记录的配置
要让上面的代码发挥作用,还需要配置文件中对history属性进行配置。
历史记录的详细程度从低到高位none,activity,audit,full。下面是使用了full进行配置的结果
只有设置为full才能使得日志可以查看到具体的变量的变化过程。