在上一節,我們進行了一個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才能使得日誌可以查看到具體的變量的變化過程。