Activiti6.0學習實踐(4)-流程引擎配置一

在上一節,我們進行了一個hello world 的簡單應用搭建,本節繼續對activiti的一些重要組件進行更進一步的分析。

目錄

1、activiti工程骨架

1.2 添加demo類到骨架工程

1.3 創建基於骨架的maven工程

2、流程引擎

2.1 ProcessEngineConifguration:

​​​​​​​2.2 activiti.cfg.xml文件的配置

​​​​​​​3、數據庫配置

3.1 支持的數據庫

​​​​​​​3.2 數據源配置:

​​​​​​​3.3  數據庫更新策略

​​​​​​​3.4 採用默認H2數據庫

​​​​​​​3.5 採用mysql數據庫

​​​​​​​3.6 採用datasource方式配置

4、日誌和數據記錄

4.1、activiti的日誌組件

​​​​​​​4.2 測試代碼

4.3 配置文件

​​​​​​​5、歷史記錄配置

​​​​​​​5.1、歷史記錄的使用

​​​​​​​5.2 歷史記錄的配置


​​​​​​​

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>

這裏幾個說明:

  1. processEngineConfiguration的實現類有多種實現,可以根據具體的場景進行選用,本例中採用的是StandaloneProcessEngineConfiguration實現。
  2. 數據庫jdbcDriver:可以支持多種數據庫
  3. 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才能使得日誌可以查看到具體的變量的變化過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章