工作流引擎詳解!工作流開源框架ACtiviti的詳細配置以及安裝和使用

攻城獅Chova 發表於 2021-05-27

工作流引擎詳解!工作流開源框架ACtiviti的詳細配置以及安裝和使用

建立ProcessEngine

  • Activiti流程引擎的配置檔案是名為activiti.cfg.xml的XML檔案.注意與使用Spring方式建立流程引擎是不一樣的
  • 使用org.activiti.engine.ProcessEngines類,獲得ProcessEngine:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()

它會在classpath下搜尋activiti.cfg.xml,並基於這個檔案中的配置構建引擎

<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="" />

    <property name="databaseSchemaUpdate" value="true" />

    <property name="jobExecutorActivate" value="false" />

    <property name="mailServerHost" value="mail.my-corp.com" />
    <property name="mailServerPort" value="5025" />
  </bean>

</beans>

配置檔案中使用的ProcessEngineConfiguration可以通過程式設計方式建立,可以配置不同的bean id

ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource);
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);	// 配置不同的bean id
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);

如果不使用配置檔案進行配置,就會基於預設建立配置

  • ProcessEngineConfiguration.createXXX() 方法都會返回ProcessEngineConfiguration,後續可以調整成所需的物件. 在呼叫buildProcessEngine()後, 就會建立一個ProcessEngine:
  ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()
  .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE)
  .setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000")
  .setJobExecutorActivate(true)
  .buildProcessEngine();

ProcessEngineConfiguration bean

  • activiti.cfg.xml必須包含一個id='processEngineConfiguration' 的bean
 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

這個bean會用來構建ProcessEngine. 有多個類可以用來定義processEngineConfiguration. 這些類對應不同的環境,並設定了對應的預設值:

  • org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration: 單獨執行的流程引擎.Activiti會自己處理事務.預設資料庫只在引擎啟動時檢測(如果沒有Activiti的表或者表結構不正確就會丟擲異常)
  • org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration: 單元測試時的輔助類.Activiti會自己控制事務. 預設使用H2記憶體資料庫,資料庫表會在引擎啟動時建立,關閉時刪除.使用它時,不需要其他配置(除非使用job執行器或郵件功能)
  • org.activiti.spring.SpringProcessEngineConfiguration: 在Spring環境下使用流程引擎
  • org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration: 單獨執行流程引擎,並使用JTA事務

資料庫配置

定義資料庫配置引數

  • 基於資料庫配置引數定義資料庫連線配置
    • jdbcUrl: 資料庫的JDBC URL
    • jdbcDriver: 對應不同資料庫型別的驅動
    • jdbcUsername: 連線資料庫的使用者名稱
    • jdbcPassword: 連線資料庫的密碼
  • 基於JDBC引數配置的資料庫連線 會使用預設的MyBatis連線池,配置MyBatis連線池:
    • jdbcMaxActiveConnections: 連線池中處於被使用狀態的連線的最大值.預設為10
    • jdbcMaxIdleConnections: 連線池中處於空閒狀態的連線的最大值
    • jdbcMaxCheckoutTime: 連線被取出使用的最長時間,超過時間會被強制回收. 預設為20000(20秒)
    • jdbcMaxWaitTime: 這是一個底層配置,讓連線池可以在長時間無法獲得連線時, 列印一條日誌,並重新嘗試獲取一個連線.(避免因為錯誤配置導致沉默的操作失敗) 預設為20000(20秒)

使用javax.sql.DataSource配置

  • Activiti的釋出包中沒有這些類, 要把對應的類放到classpath下
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/activiti" />
  <property name="username" value="activiti" />
  <property name="password" value="activiti" />
  <property name="defaultAutoCommit" value="false" />
</bean>

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

    <property name="dataSource" ref="dataSource" />
    ...
</bean>
  • 無論使用JDBC還是DataSource,都可以設定下面的配置:
    • databaseType:
      • 一般不用設定,因為可以自動通過資料庫連線的後設資料獲取
      • 只有自動檢測失敗時才需要設定.可能的值有:{h2,mysql,oracle,postgres,mssql,db2}
      • 如果沒使用預設的H2資料庫就必須設定這項.這個配置會決定使用哪些建立/刪除指令碼和查詢語句
    • databaseSchemaUpdate: 設定流程引擎啟動和關閉時如何處理資料庫表
      • false:預設, 檢查資料庫表的版本和依賴庫的版本,如果版本不匹配就丟擲異常
      • true: 構建流程引擎時,執行檢查,如果需要就執行更新. 如果表不存在,就建立
      • create-drop: 構建流程引擎時建立資料庫表,關閉流程引擎時刪除這些表

JNDI資料庫配置

  • 在預設情況下,Activiti的資料庫配置會放在web應用的WEB-INF/classes目錄下的db.properties檔案中. 這樣做比較繁瑣,因為要使用者在每次釋出時,都修改Activiti原始碼中的db.properties並重新編譯war檔案,或者解壓縮war檔案,修改其中的db.properties
  • 使用 JNDI(Java命名和目錄介面) 來獲取資料庫連線,連線是由servlet容器管理的,可以在war部署外邊管理配置. 與db.properties相比,它也允許對連線進行更多的配置

JNDI的使用

  • Activiti Explorer和Activiti Rest應用從db.properties轉換為使用JNDI資料庫配置:
    • 需要開啟原始的Spring配置檔案:
      • activiti-webapp-explorer/src/main/webapp/WEB-INF/activiti-standalone-context.xml
      • activiti-webapp-rest2/src/main/resources/activiti-context.xml
    • 刪除dbPropertiesdataSource兩個bean,然後新增如下bean:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/activitiDB"/>
</bean>
  • 我們需要新增包含了預設的H2配置的context.xml檔案
  • 如果已經有了JNDI配置,會覆蓋這些配置.對應的配置檔案activiti-webapp-explorer2/src/main/webapp/META-INF/context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/activiti-explorer2">
    <Resource auth="Container"
              name="jdbc/activitiDB"
              type="javax.sql.DataSource"
              scope="Shareable"
              description="JDBC DataSource"
              url="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000"
              driverClassName="org.h2.Driver"
              username="sa"
              password=""
              defaultAutoCommit="false"
              initialSize="5"
              maxWait="5000"
              maxActive="120"
              maxIdle="5"/>
</Context>
  • 如果是Activiti REST應用,則新增activiti-webapp-rest2/src/main/webapp/META-INF/context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/activiti-rest2">
    <Resource auth="Container"
              name="jdbc/activitiDB"
              type="javax.sql.DataSource"
              scope="Shareable"
              description="JDBC DataSource"
              url="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=-1"
              driverClassName="org.h2.Driver"
              username="sa"
              password=""
              defaultAutoCommit="false"
              initialSize="5"
              maxWait="5000"
              maxActive="120"
              maxIdle="5"/>
</Context>
  • 最後刪除Activiti Explorer和Activiti Rest兩個應用中不再使用的db.properties檔案

JNDI的配置

  • JNDI資料庫配置會因為使用的Servlet container不同而不同
  • Tomcat容器中的JNDI配置如下:
    • JNDI資源配置在CATALINA_BASE/conf/[enginename]/[hostname]/[warname].xml(對於Activiti Explorer來說,通常是在CATALINA_BASE/conf/Catalina/localhost/activiti-explorer.war) 當應用第一次釋出時,會把這個檔案從war中複製出來.所以如果這個檔案已經存在了,需要替換它.修改JNDI資源讓應用連線mysql而不是H2:
<?xml version="1.0" encoding="UTF-8"?>
    <Context antiJARLocking="true" path="/activiti-explorer2">
        <Resource auth="Container"
            name="jdbc/activitiDB"
            type="javax.sql.DataSource"
            description="JDBC DataSource"
            url="jdbc:mysql://localhost:3306/activiti"
            driverClassName="com.mysql.jdbc.Driver"
            username="sa"
            password=""
            defaultAutoCommit="false"
            initialSize="5"
            maxWait="5000"
            maxActive="120"
            maxIdle="5"/>
    </Context>

Activiti支援的資料庫

  • h2: 預設配置的資料庫
  • mysql
  • oracle
  • postgres
  • db2
  • mssql

建立資料庫表

  • 建立資料庫表的方法:
    • activiti-engine的jar放到classpath下
    • 新增對應的資料庫驅動
    • 把Activiti配置檔案(activiti.cfg.xml)放到classpath下,指向你的資料庫
    • 執行DbSchemaCreate類的main方法
SQL DDL語句可以從Activiti下載頁或Activiti釋出目錄裡找到,在database子目錄下.
指令碼也包含在引擎的jar中:activiti-engine-x.jar在org/activiti/db/create包下,drop目錄裡是刪除語句

- SQL檔案的命名方式如下:
[activiti.{db}.{create|drop}.{type}.sql]
type 是:
- engine:引擎執行的表,必須
- identity:包含使用者,群組,使用者與組之間的關係的表.這些表是可選的,只有使用引擎自帶的預設身份管理時才需要
- history:包含歷史和審計資訊的表,可選的.歷史級別設為none時不會使用. 注意這也會引用一些需要把資料儲存到歷史表中的功能

資料庫表名理解

  • Activiti的表都以ACT_開頭, 第二部分是表示表的用途的兩個字母標識.用途和服務的API對應
    • ACT_RE_*: RE表示repository. 這個字首的表包含了流程定義和流程靜態資源
    • ACT_RU_*: RU表示runtime. 這些是執行時的表,包含流程例項,任務,變數,非同步任務等執行中的資料. Activiti只在流程例項執行過程中儲存這些資料, 在流程結束時就會刪除這些記錄.這樣執行時表可以一直很小速度很快
    • ACT_ID_*: ID 表示identity. 這些表包含身份資訊. 比如使用者,組等等
    • ACT_HI_*: HI 表示history. 這些表包含歷史資料. 比如歷史流程例項, 變數,任務等等
    • ACT_GE_*: 通用資料. 用於不同場景下

資料庫升級

  • 在執行更新之前要先使用資料庫的備份功能備份資料庫
  • 預設情況下,每次構建流程引擎時都會進行版本檢測.這一切都在應用啟動或Activiti webapp啟動時發生.如果Activiti發現資料庫表的版本與依賴庫的版本不同,就會丟擲異常
  • 對activiti.cfg.xml配置檔案進行配置來升級:
<beans ... >

  <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <!-- ... -->
    <property name="databaseSchemaUpdate" value="true" />
    <!-- ... -->
  </bean>

</beans>
  • 然後,把對應的資料庫驅動放到classpath裡.升級應用的Activiti依賴,啟動一個新版本的Activiti指向包含舊版本的資料庫,將databaseSchemaUpdate設定為true,Activiti會自動將資料庫表升級到新版本
  • 當發現依賴和資料庫表版本不通過時,也可以執行更新升級DDL語句
  • 也可以執行資料庫指令碼,可以在Activiti下載頁找到

啟用Job執行器

  • JobExecutor是管理一系列執行緒的元件,可以觸發定時器(包含後續的非同步訊息).
  • 在單元測試場景下,很難使用多執行緒.因此API允許查詢Job(ManagementService.createJobQuery)執行Job (ManagementService.executeJob),
  • 因此Job可以在單元測試中控制, 要避免與job執行器衝突,可以關閉它
  • 預設,JobExecutor在流程引擎啟動時就會啟用. 如果不想在流程引擎啟動後自動啟用JobExecutor,可以設定
<property name="jobExecutorActivate" value="false" />

配置郵件伺服器

  • Activiti支援在業務流程中傳送郵件,可以在配置中配置郵件伺服器
  • 配置SMTP郵件伺服器來傳送郵件

配置歷史儲存

  • Activiti可以配置來定製歷史儲存資訊
<property name="history" value="audit" />

表示式和指令碼暴露配置

  • 預設情況下,activiti.cfg.xml和Spring配置檔案中所有bean 都可以在表示式和指令碼中使用
  • 如果要限制配置檔案中的bean的可見性,可以通過配置流程引擎配置的beans來配置
  • ProcessEngineConfiguration的beans是一個map.當指定了這個引數,只有包含這個map中的bean可以在表示式和指令碼中使用.通過在map中指定的名稱來決定暴露的bean

配置部署快取

  • 因為流程定義的資料是不會改變的,為了避免每次使用訪問資料庫,所有流程定義在解析之後都會被快取
  • 預設情況下,不會限制這個快取.如果想限制流程定義快取,可以新增如下配置
<property name="processDefinitionCacheLimit" value="10" />

這個配置會把預設的HashMap快取替換成LRU快取來提供限制. 這個配置的最佳值跟流程定義的總數有關,實際使用中會具體使用多少流程定義也有關

  • 也可以注入自定義的快取實現,這個bean必須實現org.activiti.engine.impl.persistence.deploy.DeploymentCache介面
<property name="processDefinitionCache">
  <bean class="org.activiti.MyCache" />
</property>
  • 類似的配置有knowledgeBaseCacheLimitknowledgeBaseCache, 它們是配置規則快取的.只有流程中使用規則任務時才用

日誌

  • 從Activiti 5.12開始,所有日誌(activiti,spring,,mybatis等等)都轉發給slf4j允許自定義日誌實現
  • 引入Maven依賴log4j實現,需要新增版本
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
</dependency>
  • 使用Maven的例項,忽略版本
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
</dependency>

對映診斷上下文

  • Activiti支援slf4j的MDC功能, 如下的基礎資訊會傳遞到日誌中記錄:
    • 流程定義ID: mdcProcessDefinitionID
    • 流程例項ID: mdcProcessInstanceID
    • 分支ID: mdcexecutionId
  • 預設不會記錄這些資訊,可以配置日誌使用期望的格式來顯示它們,擴充套件通常的日誌資訊. 比如,通過log4j配置定義會讓日誌顯示上面的資訊:
 log4j.appender.consoleAppender.layout.ConversionPattern =ProcessDefinitionId=%X{mdcProcessDefinitionID}
executionId=%X{mdcExecutionId}mdcProcessInstanceID=%X{mdcProcessInstanceID} mdcBusinessKey=%X{mdcBusinessKey} %m%n"

當系統進行高風險任務,日誌必須嚴格檢查時,這個功能就非常有用,要使用日誌分析的情況

事件處理

  • Activiti中實現了一種事件機制,它允許在引擎觸發事件時獲得提醒
  • 為對應的事件型別註冊監聽器,在這個型別的任何時間觸發時都會收到提醒:
    • 可以新增引擎範圍的事件監聽器,可以通過配置新增引擎範圍的事件監聽器在執行階段使用API
    • 新增event-listener到特定流程定義的BPMN XML中
  • 所有分發的事件,都是org.activiti.engine.delegate.event.ActivitiEvent的子類.事件包含type,executionId,processInstanceId和processDefinitionId. 對應的事件會包含事件發生時對應上下文的額外資訊

事件監聽器實現

  • 實現事件監聽器要實現org.activiti.engine.delegate.event.ActivitiEventListener.
  • 下面監聽器的實現會把所有監聽到的事件列印到標準輸出中,包括job執行的事件異常:
public class MyEventListener implements ActivitiEventListener {

  @Override
  public void onEvent(ActivitiEvent event) {
    switch (event.getType()) {

      case JOB_EXECUTION_SUCCESS:
        System.out.println("A job well done!");
        break;

      case JOB_EXECUTION_FAILURE:
        System.out.println("A job has failed...");
        break;

      default:
        System.out.println("Event received: " + event.getType());
    }
  }

  @Override
  public boolean isFailOnException() {
    // The logic in the onEvent method of this listener is not critical, exceptions
    // can be ignored if logging fails...
    return false;
  }
}

isFailOnException(): 決定了當事件分發時onEvent(..) 方法丟擲異常時的行為

  • 返回false,會忽略異常
  • 返回true,異常不會忽略,繼續向上傳播,迅速導致當前命令失敗
  • 當事件是一個API呼叫的一部分時(或其他事務性操作,比如job執行), 事務就會回滾
  • 當事件監聽器中的行為不是業務性時,建議返回false
  • activiti提供了一些基礎的實現,實現了事件監聽器的常用場景可以用來作為基類或監聽器實現的樣例
    • org.activiti.engine.delegate.event.BaseEntityEventListener:
      • 這個事件監聽器的基類可以用來監聽實體相關的事件,可以針對某一型別實體,也可以是全部實體
      • 隱藏了型別檢測,並提供了三個需要重寫的方法:
        • onCreate(..)
        • onUpdate(..)
        • onDelete(..)
        • 當實體建立,更新,或刪除時呼叫
      • 對於其他實體相關的事件,會呼叫onEntityEvent(..)

事件監聽器的配置安裝

  • 把事件監聽器配置到流程引擎配置中,會在流程引擎啟動時啟用,並在引擎啟動過程中持續工作
  • eventListeners屬性需要org.activiti.engine.delegate.event.ActivitiEventListener的佇列
    • 通常,我們可以宣告一個內部的bean定義,或使用ref引用已定義的bean.下面的程式碼,向配置新增了一個事件監聽器,任何事件觸發時都會提醒它,無論事件是什麼型別:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    ...
    <property name="eventListeners">
      <list>
         <bean class="org.activiti.engine.example.MyEventListener" />
      </list>
    </property>
</bean>
  • 為了監聽特定型別的事件
    • 可以使用typedEventListeners屬性
    • 它需要一個map引數
    • map的key是逗號分隔的事件名或單獨的事件名
    • map的value是org.activiti.engine.delegate.event.ActivitiEventListener佇列
  • 下面的程式碼演示了向配置中新增一個事件監聽器,可以監聽job執行成功或失敗:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    ...
    <property name="typedEventListeners">
      <map>
        <entry key="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" >
          <list>
            <bean class="org.activiti.engine.example.MyJobEventListener" />
          </list>
        </entry>
      </map>
    </property>
</bean>

分發事件的順序是由監聽器新增時的順序決定的

  • 首先,會呼叫所有普通的事件監聽器(eventListeners屬性),按照它們在list中的次序
  • 然後,會呼叫所有對應型別的監聽器(typedEventListeners屬性),對應型別的事件被觸發

執行階段新增監聽器

  • 通過API:RuntimeService, 在執行階段新增或刪除額外的事件監聽器:
/**
 * Adds an event-listener which will be notified of ALL events by the dispatcher.
 * @param listenerToAdd the listener to add
 */
void addEventListener(ActivitiEventListener listenerToAdd);

/**
 * Adds an event-listener which will only be notified when an event occurs, which type is in the given types.
 * @param listenerToAdd the listener to add
 * @param types types of events the listener should be notified for
 */
void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types);

/**
 * Removes the given listener from this dispatcher. The listener will no longer be notified,
 * regardless of the type(s) it was registered for in the first place.
 * @param listenerToRemove listener to remove
 */
 void removeEventListener(ActivitiEventListener listenerToRemove);
  • 執行階段新增的監聽器引擎重啟後就消失

流程定義新增監聽器

  • 特定流程定義新增監聽器:
    • 監聽器只會監聽與這個流程定義相關的事件以及這個流程定義上發起的所有流程例項的事件
  • 監聽器實現:
    • 可以使用全類名定義
    • 引用實現了監聽器介面的表示式
    • 配置為丟擲一個message,signal,errorBPMN事件
監聽器執行自定義邏輯
  • 下面程式碼為一個流程定義新增了兩個監聽器:
    • 第一個監聽器會接收所有型別的事件,它是通過全類名定義的
    • 第二個監聽器只接收作業成功或失敗的事件,它使用了定義在流程引擎配置中的beans屬性中的一個bean
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener class="org.activiti.engine.test.MyEventListener" />
    <activiti:eventListener delegateExpression="${testEventListener}" events="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" />
  </extensionElements>
	...
</process>
  • 對於實體相關的事件,也可以設定為針對某個流程定義的監聽器,實現只監聽發生在某個流程定義上的某個型別實體事件.下面的程式碼演示瞭如何實現這種功能:
    • 第一個例子:用於監聽所有實體事件
    • 第二個例子:用於監聽特定型別的事件
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener class="org.activiti.engine.test.MyEventListener" entityType="task" />
    <activiti:eventListener delegateExpression="${testEventListener}" events="ENTITY_CREATED" entityType="task" />
  </extensionElements>
  ...
</process>
  • entityType支援的值有:
    • attachment
    • comment
    • execution
    • identity-link
    • job
    • process-instance
    • process-definition
    • task
監聽丟擲BPMN事件
  • 另一種處理事件的方法是丟擲一個BPMN事件:
    • 只針對與丟擲一個activiti事件型別的BPMN事件, 丟擲一個BPMN事件,在流程例項刪除時,會導致一個錯誤
  • 下面的程式碼演示瞭如何在流程例項中丟擲一個signal,把signal丟擲到外部流程(全域性),在流程例項中丟擲一個訊息事件,在流程例項中丟擲一個錯誤事件.除了使用classdelegateExpression, 還使用了throwEvent屬性,通過額外屬性,指定了丟擲事件的型別
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="signal" signalName="My signal" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="globalSignal" signalName="My signal" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="message" messageName="My message" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="error" errorCode="123" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
  • 如果需要宣告額外的邏輯,是否丟擲BPMN事件,可以擴充套件activiti提供的監聽器類:
    • 在子類中重寫isValidEvent(ActivitiEvent event), 可以防止丟擲BPMN事件.對應的類是:
      • org.activiti.engine.impl.bpmn.helper.MessageThrowingEventListener
      • org.activiti.engine.test.api.event.SignalThrowingEventListenerTest
      • org.activiti.engine.impl.bpmn.helper.ErrorThrowingEventListener
流程定義監聽器注意點
  • 事件監聽器只能宣告在process元素中,作為extensionElements的子元素.監聽器不能定義在流程的單個activity下
  • delegateExpression中的表示式無法訪問execution上下文,這與其他表示式不同(比如gateway).它只能引用定義在流程引擎配置的beans屬性中宣告的bean, 或者使用spring(未使用beans屬性)中所有實現了監聽器介面的spring-bean
  • 使用監聽器的class屬性時,只會建立一個例項.監聽器實現不會依賴成員變數,是多執行緒安全的
  • 當一個非法的事件型別用在events屬性或throwEvent中時,流程定義釋出時就會丟擲異常(會導致部署失敗)
    • 如果class或delegateExecution由問題:類不存在,不存在的bean引用,或代理類沒有實現監聽器介面
      • 在流程啟動時丟擲異常
      • 在第一個有效的流程定義事件被監聽器接收時
    • 所以要保證引用的類正確的放在classpath下,表示式也要引用一個有效的例項

通過API分發事件

  • Activiti我們提供了通過API使用事件機制的方法,允許觸發定義在引擎中的任何自定義事件
  • 建議只觸發型別為CUSTOMActivitiEvents.可以通過RuntimeService觸發事件:
/**
 * Dispatches the given event to any listeners that are registered.
 * @param event event to dispatch.
 *
 * @throws ActivitiException if an exception occurs when dispatching the event or when the {@link ActivitiEventDispatcher}
 * is disabled.
 * @throws ActivitiIllegalArgumentException when the given event is not suitable for dispatching.
 */
 void dispatchEvent(ActivitiEvent event);

支援的事件型別

  • 引擎中每個事件型別都對應org.activiti.engine.delegate.event.ActivitiEventType中的一個列舉值
事件名稱 事件描述 事件型別
ENGINE_CREATED 監聽器監聽的流程引擎已經建立,準備好接受API呼叫 ActivitiEvent
ENGINE_CLOSED 監聽器監聽的流程引擎已經關閉,不再接受API呼叫 ActivitiEvent
ENTITY_CREATED 建立了一個新實體,實體包含在事件中 ActivitiEntityEvent
ENTITY_INITIALIZED 建立了一個新實體,初始化也完成了.如果這個實體的建立會包含子實體的建立,這個事件會在子實體都建立/初始化完成後被觸發,這是與ENTITY_CREATED的區別 ActivitiEntityEvent
ENTITY_UPDATED 更新了已存在的實體,實體包含在事件中 ActivitiEntityEvent
ENTITY_DELETED 刪除了已存在的實體,實體包含在事件中 ActivitiEntityEvent
ENTITY_SUSPENDED 暫停了已存在的實體,實體包含在事件中.會被ProcessDefinitions,ProcessInstances和Tasks丟擲 ActivitiEntityEvent
ENTITY_ACTIVATED 啟用了已存在的實體,實體包含在事件中.會被ProcessDefinitions,ProcessInstances和Tasks丟擲 ActivitiEntityEvent
JOB_EXECUTION_SUCCESS 作業執行成功,job包含在事件中 ActivitiEntityEvent
JOB_EXECUTION_FAILURE 作業執行失敗,作業和異常資訊包含在事件中 ActivitiEntityEvent
ActivitiExceptionEvent
JOB_RETRIES_DECREMENTED 因為作業執行失敗,導致重試次數減少.作業包含在事件中 ActivitiEntityEvent
TIMER_FIRED 觸發了定時器,job包含在事件中 ActivitiEntityEvent
JOB_CANCELED 取消了一個作業.事件包含取消的作業.作業可以通過API呼叫取消,任務完成後對應的邊界定時器也會取消,在新流程定義釋出時也會取消 ActivitiEntityEvent
ACTIVITY_STARTED 一個節點開始執行 ActivitiActivityEvent
ACTIVITY_COMPLETED 一個節點成功結束 ActivitiActivityEvent
ACTIVITY_SIGNALED 一個節點收到了一個訊號 ActivitiSignalEvent
ACTIVITY_MESSAGE_RECEIVED 一個節點收到了一個訊息.在節點收到訊息之前觸發,收到後,會觸發ACTIVITY_SIGNALACTIVITY_STARTED, 這會根據節點的型別:邊界事件,事件子流程開始事件 ActivitiMessageEvent
ACTIVITY_ERROR_RECEIVED 一個節點收到了一個錯誤事件.在節點實際處理錯誤之前觸發, 事件的activityId對應著處理錯誤的節點.這個事件後續會是ACTIVITY_SIGNALLEDACTIVITY_COMPLETE, 如果錯誤傳送成功的話 ActivitiErrorEvent
UNCAUGHT_BPMN_ERROR 丟擲了未捕獲的BPMN錯誤.流程沒有提供針對這個錯誤的處理器.事件的activityId為空 ActivitiErrorEvent
ACTIVITY_COMPENSATE 一個節點將要被補償.事件包含了將要執行補償的節點id ActivitiActivityEvent
VARIABLE_CREATED 建立了一個變數.事件包含變數名,變數值和對應的分支或任務(如果存在) ActivitiVariableEvent
VARIABLE_UPDATED 更新了一個變數.事件包含變數名,變數值和對應的分支或任務(如果存在) ActivitiVariableEvent
VARIABLE_DELETED 刪除了一個變數.事件包含變數名,變數值和對應的分支或任務(如果存在) ActivitiVariableEvent
TASK_ASSIGNED 任務被分配給了一個人員.事件包含任務 ActivitiEntityEvent
TASK_CREATED 建立了新任務.它位於ENTITY_CREATE事件之後.當任務是由流程建立時,這個事件會在TaskListener執行之前被執行 ActivitiEntityEvent
TASK_COMPLETED 任務完成.它會在ENTITY_DELETE事件之前觸發.當任務是流程一部分時,事件會在流程繼續執行之前, 後續事件將是ACTIVITY_COMPLETE,對應著完成任務的節點 ActivitiEntityEvent
TASK_TIMEOUT 任務已超時.在TIMER_FIRED事件之後,會觸發使用者任務的超時事件,當這個任務分配了一個定時器的時候 ActivitiEntityEvent
PROCESS_COMPLETED 流程已結束.在最後一個節點的ACTIVITY_COMPLETED事件之後觸發.當流程到達的狀態,沒有任何後續連線時,流程就會結束 ActivitiEntityEvent
MEMBERSHIP_CREATED 使用者被新增到一個組裡.事件包含了使用者和組的id ActivitiMembershipEvent
MEMBERSHIP_DELETED 使用者被從一個組中刪除.事件包含了使用者和組的id ActivitiMembershipEvent
MEMBERSHIPS_DELETED 所有成員被從一個組中刪除.在成員刪除之前觸發這個事件,所以他們都是可以訪問的.因為效能方面的考慮,不會為每個成員觸發單獨的MEMBERSHIP_DELETED事件 ActivitiMembershipEvent
  • 引擎內部所有ENTITY_* 事件都是與實體相關的,實體事件與實體的對應關係:
    • [ENTITY_CREATED],[ENTITY_INITIALIZED],[ENTITY_DELETED]:
      • Attachment
      • Comment
      • Deployment
      • Execution
      • Group
      • IdentityLink
      • Job
      • Model
      • ProcessDefinition
      • ProcessInstance
      • Task
      • User
    • ENTITY_UPDATED:
      • Attachment
      • Deployment
      • Execution
      • Group
      • IdentityLink
      • Job
      • Model
      • ProcessDefinition
      • ProcessInstance
      • Task
      • User
    • ENTITY_SUSPENDED, ENTITY_ACTIVATED:
      • ProcessDefinition
      • ProcessInstance
      • Execution
      • Task

注意

  • 只有同一個流程引擎中的事件會傳送給對應的監聽器
  • 如果有很多引擎在同一個資料庫執行,事件只會傳送給註冊到對應引擎的監聽器.其他引擎發生的事件不會傳送給這個監聽器,無論實際上它們執行在同一個或不同的JVM中
  • 對應的事件型別都包含對應的實體.根據型別或事件,這些實體不能再進行更新(比如,當例項以被刪除).可能的話,使用事件提供的EngineServices來以安全的方式來操作引擎.即使如此,也要小心的對事件對應的實體進行更新,操作
  • 沒有對應歷史的實體事件,因為它們都有執行階段的對應實體