3 配置

3.1 创建流程引擎

Activiti 流程引擎的配置文件是名为 activiti.cfg.xml 文件。 注意这与使用 Spring 方式创建流程引擎是不一样的。

获得 ProcessEngine 最简单的办法是 使用org.activiti.engine.ProcessEngines 类:

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="asyncExecutorEnabled" value="true" />
    <property name="asyncExecutorActivate" value="false" />

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

</beans>

注意配置 XML 文件其实是一个 Spring 的配置文件。 **但不是说 Activiti 只能用在 Spring 环境
中!** 我们只是利用了 Spring 的解析和依赖注入功能 来构建引擎。

配置文件中使用的 ProcessEngineConfiguration 可以通过编程方式创建。 可以使用不同的 bean id(比如,例子第三行)。

ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource);
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);

也可以不使用配置文件,基于默认创建配置 (参考各种支持类

ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();

所有这些 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")
  .setAsyncExecutorEnabled(true)
  .setAsyncExecutorActivate(false)
  .buildProcessEngine();

3.2 流程引擎配置 bean

activiti.cfg.xml 必须包含一个 bean, id为’processEngineConfiguration’。

<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 环境下使用流程引擎。 参考 [Chapter 5. Spring integration 集成 Spring](../Chapter 5. Spring integration 集成 Spring/README.md)。
  • org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration: 单独运行流程引擎,并使用 JTA 事务。

3.3 数据库配置

两种方式配置数据库给 Activiti 引擎使用。首先是定义数据库 JDBC 属性:

  • jdbcUrl: 数据库的 JDBC URL
  • jdbcDriver: 数据库类型的驱动实现
  • jdbcUsername: 数据库连接用户名
  • jdbcPassword: 数据库连接密码

基于 JDBC 参数配置的数据库连接 会使用默认的MyBatis连接池。 下面的参数可以用来配置连接池(来自MyBatis参数):

  • jdbcMaxActiveConnections: 连接池中处于被使用状态的连接的最大值。默认为10
  • jdbcMaxIdleConnections: 连接池中处于空闲状态的连接的最大值
  • jdbcMaxCheckoutTime: 连接被取出使用的最长时间,超过时间会被强制回收。 默认为20000(20秒)
  • jdbcMaxWaitTime: 这是一个底层配置,让连接池可以在长时间无法获得连接时, 打印一条日志,并重新尝试获取一个连接。(避免因为错误配置导致沉默的操作失败)。 默认为20000(20秒)。

数据配置示例:

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

也可以使用 javax.sql.DataSource 实现 (比如,[Apache Commons ](http://commons.apache.org/
dbcp/)的 DBCP):

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

注意,Activiti 的发布包中没有这些类。 你要自己把对应的类(比如,从 DBCP 里)放到你的 classpath 下。
无论你使用 JDBC 还是 DataSource 的方式,都可以设置下面的配置:

  • databaseType: 一般不用设置,因为可以自动通过数据库连接的元数据获取。 只有自动检测失败时才需要设置。 可能的值有:{h2, mysql, oracle, postgres, mssql, db2}。 如果没使用默认的H2数据库就必须设置这项。 这个配置会决定使用哪些创建/删除脚本和查询语句。 参考[支持数据库](Supported databases 支持的数据库.md)章节 了解支持哪些类型。
  • databaseSchemaUpdate: 设置流程引擎启动和关闭时如何处理数据库表。
    • false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。
    • true: 构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建
    • create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。

3.4 数据源配置

默认,Activiti 的数据库配置会放在 web 应用的 WEB-INF/classes 目录下的 db.properties 文件中。 这样做比较繁琐, 因为要用户在每次发布时,都修改 Activiti 源码中的 db.properties 并重新编译 war 文件, 或者解压缩 war 文件,修改其中的 db.properties。

使用 JNDI(Java Naming and Directory Interface - Java 命名和目录接口)来获取数据库连接, 连接是由 Servlet 容器管理的,可以
在 war 部署外边管理配置。 与 db.properties 相比, 它也允许对连接进行更多的配置。

3.4.1 使用

要想把 Activiti Explorer 和 Activiti Rest 应用从db.properties 转换为使用 JNDI 数据库配
置,需要打开原始的 Spring 配置文件 (activiti-webapp-explorer2/src/main/webapp/WEBINF/activiti-standalone-context.xml 和activiti-webapp-rest2/src/main/resources/
activiti-context.xml), 删除”dbProperties”和”dataSource”两个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 Explorer 来说,对应的配置文件 activiti-webapp-explorer2/src/main/webapp/META-INF/context.xml 如下所示:

<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 web 应用,添加的 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 文件了。

3.4.2 配置

JNDI 数据库配置会因为你使用的 Servlet 容器 不同而不同。 下面的配置可以在 Tomcat 中使用,但是对其他容器,请引用你使用的容器的文档。

如果使用 Tomcat,JNDI资源配置在
C A T A L I N A B A S E / c o n f / [ e n g i n e n a m e ] / [ h o s t n a m e ] / [ w a r n a m e ] . x m l A c t i v i t i E x p l o r e r 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>

3.5 支持的数据库

下面是可以引用的类型

Table 3.1. Supported databases

Activiti 数据库类型 JDBC URL示例 备注
h2 jdbc:h2:tcp://localhost/activiti 默认配置数据库
mysql jdbc:mysql://localhost:3306/activiti?autoReconnect=true 使用 mysql-connector-java 驱动进行测试
oracle jdbc:oracle:thin:@localhost:1521:xe  
postgres jdbc:postgresql://localhost:5432/activiti  
db2 jdbc:db2://localhost:50000/activiti  
mssql jdbc:sqlserver://localhost:1433/activiti  

3.6 创建数据库表

下面是创建数据库表最简单的办法:

  • 把 activiti-engine 的 jar 放到 classpath 下
  • 添加对应的数据库驱动
  • 把 Activiti 配置文件 (activiti.cfg.xml) 放到 classpath下, 指向你的数据库(参考[数据库配置](Database configuration 数据库配置.md)章节)
  • 执行 DbSchemaCreate 类的 main 方法

不过,一般情况只有数据库管理员才能执行 DDL 语句。 在生产环境,这也是最明智的选择。SQL DDL语句可以从 Activiti 下载页或 Activiti发布目录里找到,在 database 子目录下。 脚本
也包含在引擎的 jar 中(activiti-engine-x.jar), 在
org/activiti/db/create包下(drop 目录里是删除语句)。 SQL 文件的命名方式如下

activiti.{db}.{create|drop}.{type}.sql

其中 db 是 [支持的数据库](Supported databases 支持的数据库.md), type 是

  • engine: 引擎执行的表。必须。
  • identity: 包含用户,群组,用户与组之间的关系的表。 这些表是可选的,只有使用引擎自带的默认身份管理时才需要。
  • history: 包含历史和审计信息的表。可选的:历史级别设为 none 时不会使用。 注意这也会引用一些需要把数据保存到历史表中的功能(比如任务的评论)。

MySQL用户需要注意: 版本低于 5.6.4 的 MySQL 不支持毫秒精度的 timestamp 或 date 类型。 更严重的是,有些版本会在尝试创建这样一列时抛出异常,而有些版本则不会。 在执行自动创建/更新时,引擎会在执行过程中修改 DDL。 当使用 DD L时,可以选择通用版本和名为 mysql55的文件。 (它适合所有版本低于5.6.4的情况)。 后一个文件会将列的类型设置为没有毫秒的情况。

总结一下,对于MySQL版本会执行如下操作

  • <5.6: 不支持毫秒精度。可以使用 DDL 文件(包含 mysql55 的文件)。可以实现自动创建/更新。
  • 5.6.0 - 5.6.3: 不支持毫秒精度。无法自动创建/更新。建议更新到新的数据库版本。如果真的需要的话,也可以使用 mysql 5.5。
  • 5.6.4+:支持毫秒精度。可以使用DDL文件(默认包含 mysql 的文件)。可以实现自动创建、更新。

注意对于已经更新了 MySQL 数据库,而且 Activiti 表已经创建/更新的情况, 必须手工修改列的类型。

3.7 理解数据库表名字

Activiti 的表都以 ACT_ 开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的 API 对应。

  • ACT_RE_*: ‘RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
  • ACT_RU_*: ‘RU’表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
  • ACT_ID_*: ‘ID’表示 identity。 这些表包含身份信息,比如用户,组等等。
  • ACT_HI_*: ‘HI’表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
  • ACT_GE_*: general 数据, 用于不同场景下。

3.8 数据库升级

在执行更新之前要先备份数据库(使用数据库的备份功能)

默认,每次构建流程引擎时都会进行版本检测。 这一切都在应用启动或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 下载页找到。

3.10 启用Job执行器

JobExecutor 是管理一系列线程的组件,可以触发定时器(也包含后续的异步消息)。 在单元测试场景下,很难使用多线程。因此 API 允许查询(ManagementService.createJobQuery) 和执行 job
(ManagementService.executeJob),所以 job 可以在单元测试中控制。 要避免与 job 执行器冲突,可以关闭它。

默认,JobExecutor 在流程引擎启动时就会激活。 如果不想在流程引擎启动后自动激活 JobExecutor,可以设置

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

当流程引擎引导时,不想激活 JobExecutor。

3.11启用Async执行器

AsyncExecutor 是管理线程池的组件,可以触发定时器和异步任务。

默认,AsyncExecutor 是不启用的,由于遗留原因使用的是 JobExecutor。不过建议使用新的 AsyncExecutor 来代替。可以通过定义两个属性

<property name="asyncExecutorEnabled" value="true" />
<property name="asyncExecutorActivate" value="true" />

asyncExecutorEnabled 属性启用 Async executor 代替旧的 Job executor。第二个属性 asyncExecutorActivate 指示 Activiti 引擎在启动时启动 Async executor 线程池。

3.12 配置邮件服务器

可以选择配置邮件服务器。Activiti 支持在业务流程中发送邮件。 想真正的发送一个 e-mail,必须配置一个真实的 SMTP 邮件服务器。 参考 e-mail 任务

3.13 配置历史

可以选择定制历史存储的配置。你可以通过配置影响引擎的[历史功能](../Chapter 11. History 历史/README.md)。 参考 [History configuration 配置](../Chapter 11. History 历史/History configuration 配置.md)这一节。

<property name="history" value="audit" />

3.14 在表达式和脚本中暴露配置

默认,activiti.cfg.xml 和你自己的 Spring 配置文件中所有 bean 都可以在表达式和脚本中使用。 如果你想限制配置文件中的 bean 的可见性, 可以配置流程引擎配置的 bean 配置。
ProcessEngineConfiguration 的 beans 是一个 map。当你指定了这个参数, 只有包含这个 map 中的 bean 可以在表达式和脚本中使用。 通过在map 中指定的名称来决定暴露的 bean。

3.15 配置部署缓存

所有流程定义都被缓存了(解析之后)避免每次使用前都要访问数据库, 因为流程定义数据是不会改变的。 默认,不会限制这个缓存。如果想限制流程定义缓存,可以添加如下配置

<property name="processDefinitionCacheLimit" value="10" />

这个配置会把默认的 hashmap 缓存替换成 LRU 缓存,来提供限制。 当然,这个配置的最佳值跟流程定义的总数有关, 实际使用中会具体使用多少流程定义也有关。
也你可以注入自己的缓存实现。这个 bean 必须实现
org.activiti.engine.impl.persistence.deploy.DeploymentCache 接口:

<property name="processDefinitionCache">
  <bean class="org.activiti.MyCache" />
</property>

有一个类似的配置叫 knowledgeBaseCacheLimit 和knowledgeBaseCache, 它们是配置规则缓存的。只
有流程中使用规则任务时才会用到。

3.16 日志

从 Activiti 5.12 开始,SLF4J 被用作日志框架,替换了之前使用java.util.logging。 所有日志(activiti, spring, mybatis等等)都转发给 SLF4J 允许使用你选择的日志实现。

默认 activiti-engine 依赖中没有提供 SLF4J 绑定的jar, 需要根据你的实际需要使用日志框架。如果没有添加任何实现 jar,SLF4J 会使用NOP-logger,不使用任何日志,不会发出警告,而且什么日志都不会记录。 可以通过 http://www.slf4j.org/codes.html#StaticLoggerBinder 了解这些实现。
使用 Maven,比如使用一个依赖(这里使用 log4j),注意你还需要添加一个 version:
配置


org.slf4j
slf4j-log4j12

activiti-explorer 和 activiti-rest 应用都使用了 Log4j 绑定。执行所有 activiti-* 模块的单元测试页使用了 Log4j。

特别提醒如果容器 classpath 中存在 commons-logging: 为了把spring 日志转发给 SLF4J,需要使用桥接(参考 http://www.slf4j.org/legacy.html#jclOverSLF4J)。 如果你的容器提
供了commons-logging实现,请参考下面网页: http://www.slf4j.org/codes.html#release 来确保稳定性。
使用 Maven 的实例(忽略版本):

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
</dependency>

activiti-explorer 和 activiti-rest 应用都使用了 Log4j 绑定。执行所有 activiti-* 模块的单元测试页使用了 Log4j。

特别提醒如果容器 classpath 中存在 commons-logging: 为了把spring 日志转发给 SLF4J,需要使用桥接(参考 http://www.slf4j.org/legacy.html#jclOverSLF4J)。 如果你的容器提供了 commons-logging 实现,请参考下面网页:http://www.slf4j.org/codes.html#release 来确保稳定性。

使用 Maven 的实例(忽略版本):

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
</dependency>

3.17 映射诊断上下文

在5.13中,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"

当系统进行高风险任务,日志必须严格检查时,这个功能就非常有用,比如要使用日志分析的情况。

3.18 事件处理

Activiti 5.15 中实现了一种事件机制。它允许在引擎触发事件时获得提醒。 参考[所有支持的事件类型](#Supported event types 支持的事件类型)了解有效的事件。

可以为对应的事件类型注册监听器,在这个类型的任何时间触发时都会收到提醒。 你可以添加引擎范围的事件监听器通过[配置](#Configuration and setup 配置和安装), 添加引擎范围的事件监听器在[运行阶段使用API](#Adding listeners at runtime 运行时添加监听), 或添加 event-listener 到[特定流程定义的 BPMN XML 中](#Adding listeners to process definitions 添加监听到流程定义)。

所有分发的事件,都是org.activiti.engine.delegate.event.ActivitiEvent 的子类。事件包含(如果有效)type,executionId,processInstanceId 和processDefinitionId。 对应的事件会包含事件发生时对应上下文的额外信息, 这些额外的载荷可以在[所有支持的事件类型](#Supported event types 支持的事件类型)中找到。

3.18.1 事件监听实现

实现事件监听器的唯一要求是实现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(..)。

3.18.2 配置和安装

把事件监听器配置到流程引擎配置中时,会在流程引擎启动时激活,并在引擎启动启动中持续工作着。

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 属性),如果对应类型的事件被触发了。

3.18.3 运行时添加监听

可以通过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);

注意运行期添加的监听器引擎重启后就消失了

3.18.4 添加监听到流程定义

可以为特定流程定义添加监听器。监听器只会监听与这个流程定义相关的事件,以及这个流程定义上发起的所有流程实例的事件。 监听器实现可以使用,全类名定义,引用实现了监听器接口的表达式,或配置为抛出一个message/signal/error 的 BPMN 事件。

3.18.4.1 让监听器执行用户定义的逻辑

下面代码为一个流程定义添加了两个监听器。第一个监听器会接收所有类型的事件,它是通过全类名定义的。 第二个监听器只接收作业成功或失败的事件,它使用了定义在流程引擎配置中的 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。

<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>

3.18.4.2 Listeners throwing BPMN events

[试验阶段]

另一种处理事件的方法是抛出一个 BPMN 事件。请注意它只针对与抛出一个activiti 事件类型的 BPMN 事件。 比如,抛出一个 BPMN 事件,在流程实例删除时,会导致一个错误。 下面的代码演示了如何在流程实例中抛出一个 signal,把 signal 抛出到外部流程(全局),在流程实例中抛出一个消息事件, 在流程实例中抛出一个错误事件。除了使用 class 或delegateExpression, 还使用了 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.test.api.event.SignalThrowingEventListenerTest,
org.activiti.engine.impl.bpmn.helper.MessageThrowingEventListener 和
org.activiti.engine.impl.bpmn.helper.ErrorThrowingEventListener.

3.18.4.3 流程定义中监听器的注意事项

  • 事件监听器只能声明在 process 元素中,作为 extensionElements 的子元素。 监听器不能定义在流程的单个 activity 下。
  • delegateExpression 中的表达式无法访问 execution 上下文,这与其他表达式不同(比如 gateway )。 它只能引用定义在流程引擎配置的beans 属性中声明的 bean,或者使用 spring(未使用 beans 属性)中所有实现了监听器接口的 spring-bean。
  • 在使用监听器的 class 属性时,只会创建一个实例。记住监听器实现不会依赖成员变量,确认是多线程安全的。
  • 当一个非法的事件类型用在 events 属性或 throwEvent 中时,流程定义发布时就会抛出异常。(会导致部署失败)。如果 class 或delegateExecution 由问题(类不存在,不存在的 bean 引用,或代理类没有实现监听器接口),会在流程启动时抛出异常(或在第一个有效的流程定义事件被监听器接收时)。所以要保证引用的类正确的放在 classpath 下,表达式也要引用一个有效的实例。

3.18.5 通过 API 分发事件

我们提供了通过 API 使用事件机制的方法,允许大家触发定义在引擎中的任何自定义事件。
建议(不强制)只触发类型为 CUSTOM 的 ActivitiEvents。可以通过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);

3.18.6 支持的事件类型

下面是引擎中可能出现的所有事件类型。每个类型都对应 org.activiti.engine.delegate.event.ActivitiEventType 中的一个枚举值

Table 1. Supported events

Event 名称 描述 Event 类
ENGINE_CREATED 监听器监听的流程引擎已经创建 完毕,并准备好接受API调用。 org.activiti...ActivitiEvent
ENGINE_CLOSED 监听器监听的流程引擎已经关 闭,不再接受API调用。 org.activiti...ActivitiEvent
ENTITY_CREATED 创建了一个新实体。实体包含在 事件中。 org.activiti...ActivitiEntityEvent
ENTITY_INITIALIZED 创建了一个新实体,初始化也完成了。如果这个实体的创建会包含子实体的创建,这个事件会在子实体都创建/初始化完成后被触发,这是与ENTITY_CREATED的区别。ENTITY_CREATE event. org.activiti...ActivitiEntityEvent
ENTITY_UPDATED 更新了已存在的实体。实体包含 在事件中。 org.activiti...ActivitiEntityEvent
ENTITY_DELETED 删除了已存在的实体。实体包含 在事件中。 org.activiti...ActivitiEntityEvent
ENTITY_SUSPENDED 暂停了已存在的实体。实体包含 在事件中。会被 ProcessDefinitions,ProcessInstances 和 Tasks抛出。 org.activiti...ActivitiEntityEvent
ENTITY_ACTIVATED 激活了已存在的实体,实体包含 在事件中。会被 ProcessDefinitions, ProcessInstances 和 Tasks抛出。 org.activiti...ActivitiEntityEvent
JOB_EXECUTION_SUCCESS 作业执行成功。job包含在事件中。 org.activiti...ActivitiEntityEvent
JOB_EXECUTION_FAILURE 作业执行失败。作业和异常信息包含在事件中。 org.activiti...ActivitiEntityEvent and org.activiti...ActivitiExceptionEvent
JOB_RETRIES_DECREMENTED 因为作业执行失败,导致重试次 数减少。作业包含在事件中。 org.activiti...ActivitiEntityEvent
TIMER_FIRED 触发了定时器。job包含在事件 中。 org.activiti...ActivitiEntityEvent
JOB_CANCELED 取消了一个作业。事件包含取消 的作业。作业可以通过API调用取消, 任务完成后对应的边界定时器也会取消,在新流程定义发布时也会取消。 org.activiti...ActivitiEntityEvent
ACTIVITY_STARTED 一个节点开始执行 org.activiti...ActivitiActivityEvent
ACTIVITY_COMPLETED 一个节点成功结束 org.activiti...ActivitiActivityEvent
ACTIVITY_CANCELLED 要取消一个节点。取消是因为三个原因(MessageEventSubscriptionEntity, SignalEventSubscriptionEntity, TimerEntity) org.activiti…​ActivitiActivityCancelledEvent
ACTIVITY_SIGNALED 一个节点收到了一个信号 org.activiti...ActivitiSignalEvent
ACTIVITY_MESSAGE_RECEIVED 一个节点收到了一个消息。在节点收到消息之前触发。收到后,会触发 ACTIVITY_SIGNAL 或ACTIVITY_STARTED,这会根据节点的类型(边界事件,事件子流程开始事件)
ACTIVITY_ERROR_RECEIVED 一个节点收到了一个错误事件。在节点实际处理错误之前触发。事件的activityId对应着处理错误的节点。 这个事件后续会是ACTIVITY_SIGNALLED或ACTIVITY_COMPLETE,如果错误发送成功的话。
UNCAUGHT_BPMN_ERROR 抛出了未捕获的BPMN错误。流程 没有提供针对这个错误的处理器。 事件的activityId为空。 org.activiti...ActivitiErrorEvent
ACTIVITY_COMPENSATE 一个节点将要被补偿。事件包含 了将要执行补偿的节点id。 org.activiti...ActivitiActivityEvent
VARIABLE_CREATED 创建了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti...ActivitiVariableEvent
VARIABLE_UPDATED 更新了一个变量。事件包含变量 名,变量值和对应的分支或任务(如果存在)。 org.activiti...ActivitiVariableEvent
VARIABLE_DELETED 删除了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在) org.activiti...ActivitiVariableEvent
TASK_ASSIGNED

3.18.7 附加信息

只有事件被发送,对应的引擎内的监听器才会被通知到。你有很多引擎 - 在同一个数据库运行 - 事件只会发送给注册到对应引擎的监听器。其他引擎发生的事件不会发送给这个监听器,无论实际上它们运行在同一个或不同的JVM中。

对应的事件类型(对应实体)都包含对应的实体。根据类型或事件,这些实体不能再进行更新(比如,当实例以被删除)。可能的话,使用事件提供的 EngineServices 来以安全的方式来操作引擎。即使如此,你需要小心的对事件对应的实体进行更新/操作。

没有对应历史的实体事件,因为它们都有运行阶段的对应实体。

猜你喜欢

转载自blog.csdn.net/weixin_42068560/article/details/80365318
今日推荐