Leo's blog Leo's blog
首页
  • Java基础

    • JavaSE
    • JVM
    • Spring
  • Java进阶

    • SSM
    • SpringBoot
    • SpringCloud
    • SpringCloud Alibaba
  • 中间件

    • Nginx
    • Redis
    • FastDFS
    • RocketMQ
  • 框架

    • Netty
    • Shiro
    • Activiti
  • 数据库

    • MySQL
    • Oracle
  • 微服务

    • Dubbo
    • Nacos
  • 大叔据

    • ELK
    • Hadoop
  • 工具

    • Jenkins
    • Docker
  • 算法

    • 数据结构与算法
    • 设计模式
  • 计算机科学

    • 计算机网络
    • 操作系统原理
    • Linux
    • 网络安全
  • IDLE

    • IntelliJ IDEA
    • Visual Studio Code
  • 野生技能

    • Git
  • 娱乐

    • 读书
    • 电影
    • 音乐
    • 节目
  • 投资

    • 宏观
    • 周期
    • 加密货币
    • 量化交易
  • 思考

    • 观点
    • 随笔
  • 网站

    • 日常访问
    • 学习资源
  • 博客

    • 推荐博客
    • 友情链接
关于
  • 分类
  • 标签
  • 归档
GitHub

Leo Lee

Use Java with love.
首页
  • Java基础

    • JavaSE
    • JVM
    • Spring
  • Java进阶

    • SSM
    • SpringBoot
    • SpringCloud
    • SpringCloud Alibaba
  • 中间件

    • Nginx
    • Redis
    • FastDFS
    • RocketMQ
  • 框架

    • Netty
    • Shiro
    • Activiti
  • 数据库

    • MySQL
    • Oracle
  • 微服务

    • Dubbo
    • Nacos
  • 大叔据

    • ELK
    • Hadoop
  • 工具

    • Jenkins
    • Docker
  • 算法

    • 数据结构与算法
    • 设计模式
  • 计算机科学

    • 计算机网络
    • 操作系统原理
    • Linux
    • 网络安全
  • IDLE

    • IntelliJ IDEA
    • Visual Studio Code
  • 野生技能

    • Git
  • 娱乐

    • 读书
    • 电影
    • 音乐
    • 节目
  • 投资

    • 宏观
    • 周期
    • 加密货币
    • 量化交易
  • 思考

    • 观点
    • 随笔
  • 网站

    • 日常访问
    • 学习资源
  • 博客

    • 推荐博客
    • 友情链接
关于
  • 分类
  • 标签
  • 归档
GitHub
  • Nginx

  • Redis

  • Activiti

    • Activiti - 最成熟的JAVA工作流框架
    • Activiti使用手册(1)- 案例与配置
      • 2、案例分析
        • 1)案例:审批流程模型化
        • 2)案例:常见的网上购物流程
      • 3、上手操作
        • 1)设计工作流及协同工作
      • 1、创建流程引擎相关配置
        • 1)Activiti6 提供四种方式来配置流程
        • 2)涉及类
        • 3)涉及子类
        • 4)创建配置对象的静态方法
      • 2、数据库配置
        • 1)Activiti6 提供两种方式来配置数据库
        • 2)创建数据库配置的方法
        • 3)配置数据库更新策略,DatabaseSchemaUpdate
        • 4)附:配置DataBase Type的方法
      • 3、日志记录配置
        • 1)日志组件的关系及MDC
        • 2)配置历史记录日志级别(HistoryLevel)
        • 3)配置基于DB的事件日志(Event Logging)
      • 4、历史记录配置
        • 1)配置示例
        • 2)代码示例
        • 3)示例结果
      • 5、事件处理及监听器配置
        • 1)事件处理配置示例
        • 2)事件处理代码示例
        • 3)事件处理示例结果
        • 4)监听器概念
        • 5)监听器注册方式一
        • 6)监听器注册方式二
        • 7)自定义监听
        • 8)通过代码注册监听
        • 9)OnEvent源码
      • 6、命令拦截器配置
        • 1)命令模式与责任链模式
        • 2)拦截器的配置方式
        • 3)示例结果
        • 4)MDCCommandInvoker
      • 7、作业执行器配置
        • 1)作业执行器配置
        • 2)配置自定义线程池
        • 3)流程定时任务配置
        • 4)定时任务事件监听
        • 5)示例结果
      • 8、Activiti与Spring集成
        • 1)相关配置
        • 2)功能特征
        • 3)单元测试
        • 4)配置示例
    • Activiti使用手册(2)- Java API
    • Activiti使用手册(3)- 数据库设计
    • Activiti使用手册(4)- Bpmn2规范
  • MySQL

  • Oracle

  • Docker

  • 基础设施
  • Activiti
leee
2020-06-11

Activiti使用手册(1)- 案例与配置

# Activiti使用手册(1)- 案例与配置

# 一、概述

# 二、基本使用

# 2、案例分析

# 1)案例:审批流程模型化

# ①现实生活中的审批

image.png

# ②简单的审批流程

image.png

# ③Activiti模型分析

image.png

# 2)案例:常见的网上购物流程

# ①现实生活的购物流程

image.png

# ②购物流程模型化

image.png

# ③Activiti模型分析

image.png

# 3、上手操作

# 1)设计工作流及协同工作

# ①简单审批流程设计

  1. 设计一个二级审批流程

image.png

  1. 创建三个不同的用户作为流程参与者

image.png

  1. 参与者协同完成流程

# 三、Activiti配置

# 1、创建流程引擎相关配置

# 1)Activiti6 提供四种方式来配置流程

  • 指定目录加载一个资源配置
  • 通过流加载一个资源配置
  • 独立的引擎配置
  • 通过内存数据库构造对象配置

# 2)涉及类

ProcessEngineConfigUration 引擎配置类,作用如下:

  • 查找并解析xml配置文件activiti.cfrg.xml
  • 提供多个静态方法配置对象

# 3)涉及子类

ProcessEngineConfiguration
**ProcessEngineConfigurationImpl **抽象类配置了ProcessEngineConfiguration的户型
**StandaloneProcessEngineConfiguration **通过new的方式创建对象
**SpringProcessEngineConfiguration **基于spring集成完成扩展

# 4)创建配置对象的静态方法

  • 默认路径加载一个资源配置文件构造对象

ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault()

  • 指定目录加载一个资源配置文件构造对象

ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String)

  • 指定目录和修改id类型加载一个资源配置文件构造对象

ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String,String)

  • 通过流加载一个资源配置文件构造对象

ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream)

  • 通过流和修改id类型加载一个资源配置文件构造对象

ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream,String)

  • 独立的引擎配置对象

ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration()

  • 通过内存数据库构造对象

ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()

# 2、数据库配置

# 1)Activiti6 提供两种方式来配置数据库

  • 通过JDBC配置(MyBatis)
  • 通过数据库连接池配置(Druid、Dbcp、HikariCP)
    • Druid 为监控而生的数据库链接池来自阿里
    • Dbcp 老牌数据源链接池,稳定可靠,Tomcat自带
    • HikarIcp 来自日本的极速数据源链接池,spring默选

# 2)创建数据库配置的方法

  • 通过JDBC配置(MyBatis),默认配置
<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;autoReconnect=true&amp;failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
  </bean>
</beans>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 通过Druid配置
<?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="databaseSchemaUpdate" value="true"/>
    <!--数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--是否需要历史数据-->
    <property name="dbHistoryUsed" value="true"/>
    <!--是否需要用户数据-->
    <property name="dbIdentityUsed" value="true"/>
    <!--给表加前缀-->
    <property name="databaseTablePrefix" value="T_"/>-
  </bean>

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" 
              value="jdbc:mysql://127.0.0.1:3306/activiti6unit?useUnicode=true&amp;
                     characterEncoding=utf8&amp;useSSL=false&amp;autoReconnect=true&amp;
                     failOverReadOnly=false"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="initialSize" value="2"/>
    <property name="maxActive" value="10"/>
    <property name="filters" value="stat,slf4j"/>
  </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 3)配置数据库更新策略,DatabaseSchemaUpdate

  • false:启动时检查数据库版本,发生不匹配抛异常,适用于生产环境
  • true:启动时自动检查并更新数据库表,不存在创建,适用与开发环境
  • create-drop:启动时创建数据库表结构,结束时删除表结构,适用于测试环境

# 4)附:配置DataBase Type的方法

image.png

# 3、日志记录配置

# 1)日志组件的关系及MDC

  • 日志组件的关系

image.png

  • 配置开启MDC

MDC:Mapped Diagnostic Context, 映射诊断环境
MDC使用方法1:LogMDC.setMDCEnabled(true); 当出现异常时会在日志显示流程id等信息

public class ConfigMDCTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigMDCTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_mdc.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        LogMDC.setMDCEnabled(true);

        ProcessInstance processInstance = activitiRule
            .getRuntimeService()
            .startProcessInstanceByKey("my-process");
        assertNotNull(processInstance);
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        assertEquals("Activiti is awesome!", task.getName());
        activitiRule.getTaskService().complete(task.getId());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

MDC使用方法2:通过拦截器设置MDC

<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?
                                    useUnicode=true&amp;characterEncoding=utf8&amp;
                                    useSSL=false&amp;autoReconnect=true&amp;
                                    failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
    <!--自定义拦截每次执行流程都打印流程id-->
    <property name="commandInvoker" ref="commandInvoker"/>
  </bean>
  <bean id="commandInvoker" class="com.guosh.activiti.interceptor.MDCCommandInvoker"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MDCCommandInvoker extends DebugCommandInvoker {

    private static final Logger logger = LoggerFactory.getLogger(DebugCommandInvoker.class);

    @Override
    public void executeOperation(Runnable runnable) {
        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.clear();
        if(!mdcEnabled){
            LogMDC.setMDCEnabled(false);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

MDC使用方法3:配置logback.xml日志模板 %X{mdcProcessInstanceId}

记录结果示例:
image.png

# 2)配置历史记录日志级别(HistoryLevel)

配置流程结束后的历史记录分级

  • none: 不记录历史流程性能高,流程结束后不可读取
  • activiti: 归档流程实例,流程变量不同步 (能看到历史内容历史活动)
  • audit: 默认值,在activiti基础上同步变量值,保存表单属性 (能看到历史表单,历史详情)
  • full: 性能较差,记录所有实例和变量细节变化 (上面基础上能看到详情的变化)

示例操作请跳转至[]

# 3)配置基于DB的事件日志(Event Logging)

注意事项

  • 这是实验性的记录机制,性能影响较大(数据库和应用服务器)
  • 开启后默认记录所有变化过程,表记录快速增长
  • 内容存储格式是基于JSON格式的,适用于MongoDB,ElasticSearch

# 4、历史记录配置

# 1)配置示例

<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="jdbcUrl" 
              value="jdbc:mysql://127.0.0.1:3306/activiti6unit?useUnicode=true&amp;
                     characterEncoding=utf8&amp;useSSL=false&amp;
                     autoReconnect=true&amp;failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
    <!--自定义拦截每次执行流程都打印流程id-->
    <property name="commandInvoker" ref="commandInvoker"/>
    <!--查看历史的级别-->

    <!--所有历史都看不到-->
    <!--<property name="history" value="none"/>-->
    <!--能查看历史活动历史变量-->
    <!--<property name="history" value="activity"/>-->
    <!--能查看到所有,但是查看不到数据的修改记录-->
    <!--<property name="history" value="audit"/>-->
    <property name="history" value="full"/>
    
  </bean>
  <bean id="commandInvoker" class="com.guosh.activiti.interceptor.MDCCommandInvoker"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 2)代码示例

public class ConfigMDCTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigMDCTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_history.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        //启动流程
        startProcessInstance();

        //修改变量
        changeVariable();

        //提交表单task
        submitTaskForm();


        //输出历史内容
        //输出历史活动
        showHistoryActivity();

        //历史变量
        showHistoryVaiable();

        //输出历史用户任务
        showHistoryTask();

        //历史表单详情
        showHistoryForm();


        //输出历史详情
        showHistoryDetail();
    }

    //输出历史详情
    private void showHistoryDetail() {
        List<HistoricDetail> historicDetails = activitiRule.getHistoryService()
            .createHistoricDetailQuery().list();
        for (HistoricDetail historicDetail:historicDetails) {
            logger.info("historicDetail = {}",historicDetail);
        }
        logger.info("historicDetails.size = {}",historicDetails.size());
    }

    //历史表单详情
    private void showHistoryForm() {
        List<HistoricDetail> historicDetailsFrom = activitiRule.getHistoryService()
            .createHistoricDetailQuery().formProperties().list();
        for (HistoricDetail historicDetail:historicDetailsFrom) {
            logger.info("historicDetail = {}",historicDetail);
        }
        logger.info("historicDetailsFrom.size = {}",historicDetailsFrom.size());
    }

    //输出历史用户任务
    private void showHistoryTask() {
        List<HistoricTaskInstance> historicTaskInstances = activitiRule
            .getHistoryService().createHistoricTaskInstanceQuery().list();
        for (HistoricTaskInstance historicTaskInstance:historicTaskInstances) {
            logger.info("historicTaskInstance = {}",historicTaskInstance);
        }
        logger.info("historicTaskInstances.size = {}",historicTaskInstances.size());
    }

    //历史变量
    private void showHistoryVaiable() {
        List<HistoricVariableInstance> historicVariableInstances = activitiRule
            .getHistoryService().createHistoricVariableInstanceQuery().list();
        for (HistoricVariableInstance historicActivityInstance:historicVariableInstances){
            logger.info("historicActivityInstance = {}",historicActivityInstance);
        }
        logger.info("historicVariableInstances.size = {}",historicVariableInstances.size());
    }

    //输出历史活动
    private void showHistoryActivity() {
        List<HistoricActivityInstance> historicActivityInstances = activitiRule
            .getHistoryService().createHistoricActivityInstanceQuery().list();
        for (HistoricActivityInstance historicActivityInstance:historicActivityInstances){
            logger.info("historicActivityInstance = {}"
                        ,historicActivityInstance);
        }
        logger.info("historicActivityInstance.size = {}"
                    ,historicActivityInstances.size());
    }

    //
    //提交表单task
    private void submitTaskForm() {
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        Map<String,String> properties= Maps.newHashMap();
        properties.put("formKey1","valuef1");
        properties.put("formKey2","valuef2");
        activitiRule.getFormService().submitTaskFormData(task.getId(),properties);
    }

    //修改变量
    private void changeVariable() {
        List<Execution> executions = activitiRule.getRuntimeService()
            .createExecutionQuery().listPage(0, 100);
        for (Execution execution:executions) {
            logger.info("execution = {}",execution);
        }
        logger.info("execution.size = {}", executions.size());
        String id=executions.iterator().next().getId();
        activitiRule.getRuntimeService().setVariable(id,"keyStart1","value1_");
    }

    //启动流程
    private void startProcessInstance() {
        Map<String,Object> params= Maps.newHashMap();
        params.put("keyStart1","value1");
        params.put("keyStart2","value2");

        ProcessInstance processInstance = activitiRule.getRuntimeService()
            .startProcessInstanceByKey("my-process",params);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

# 3)示例结果

image.png

image.png

image.png

image.png

# 5、事件处理及监听器配置

  • 作用:监听变化 并记录变化日志
  • 方法:在XML中配置 enableDatabaseEventLogging

# 1)事件处理配置示例

<?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="jdbcUrl" 
              value="jdbc:mysql://127.0.0.1:3306/activiti6unit?useUnicode=true&amp;
                     characterEncoding=utf8&amp;useSSL=false&amp;
                     autoReconnect=true&amp;failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
    <!--自定义拦截每次执行流程都打印流程id-->
    <property name="commandInvoker" ref="commandInvoker"/>
    <!--事件监听器-->
    <property name="enableDatabaseEventLogging" value="true"/>

  </bean>
  <bean id="commandInvoker" class="com.guosh.activiti.interceptor.MDCCommandInvoker"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 2)事件处理代码示例

public class ConfigEventLogTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigEventLogTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_evenlog.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        ProcessInstance processInstance = activitiRule.getRuntimeService()
            .startProcessInstanceByKey("my-process");
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        assertEquals("Activiti is awesome!", task.getName());
        activitiRule.getTaskService().complete(task.getId());
        //根据流程id查询
        List<EventLogEntry> envenLogEntrys = activitiRule.getManagementService()
                .getEventLogEntriesByProcessInstanceId(processInstance.getId());

        for (EventLogEntry eventLogEntry:envenLogEntrys) {
            logger.info("eventLogEntry.type = {},eventLogEntry.data = {}"
                        ,eventLogEntry.getType(),eventLogEntry.getData());

        }
        logger.info("envenLogEntrys.sice = {},eventLogEntry.data = {}"
                    ,envenLogEntrys.size());

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 3)事件处理示例结果

image.png

image.png

# 4)监听器概念

  • 事件及监听器原理

image.png

  • 监听器配置方式
    • 配置Listener
      • eventListeners:监听所有事件派发通知
      • typedEventListeners:监听指定事件类型的通知
      • activiti:eventListener:只监听特定的流程定义事件

**

  • Activiti的事件监听(举例)

image.png

  • 相关API
    • ActivitiEvent:事件对象
    • ActivitiEventListener:监听器
    • ActivitiEventType:事件类型

# 5)监听器注册方式一

<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?
                                    useUnicode=true&amp;characterEncoding=utf8&amp;
                                    useSSL=false&amp;autoReconnect=true&amp;
                                    failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
    <!--第一种方式-->
    <property name="eventListeners">
      <list>
        <bean class="com.guosh.activiti.event.ProcessEventListener"/>
      </list>
    </property>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ProcessEventListener implements ActivitiEventListener {
    private static  final Logger logger= LoggerFactory
        .getLogger(ProcessEventListener.class);

    @Override
    public void onEvent(ActivitiEvent event) {
        ActivitiEventType eventType = event.getType();
        //判断如果是流程启动
        if(ActivitiEventType.PROCESS_STARTED.equals(eventType)){
            logger.info("流程启动 {} \t {}",event.getType(),event.getProcessInstanceId());
        }
        //流程结束
        else if(ActivitiEventType.PROCESS_COMPLETED.equals(eventType)){
            logger.info("流程结束 {} \t {}",event.getType(),event.getProcessInstanceId());
        }
    }

    @Override
    public boolean isFailOnException() {
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 6)监听器注册方式二

    <property name="typedEventListeners">
      <map>
        <!--流程启动-->
        <entry key="PROCESS_STARTED">
          <list>
            <bean class="com.guosh.activiti.event.ProcessEventListener"/>
          </list>
        </entry>
      </map>
    </property>
1
2
3
4
5
6
7
8
9
10

# 7)自定义监听

public class ConfigEventListenerTest {
    private static  final Logger logger= LoggerFactory
        .getLogger(ConfigEventListenerTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_evenlistener.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        ProcessInstance processInstance = activitiRule.getRuntimeService()
            .startProcessInstanceByKey("my-process");
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        assertEquals("Activiti is awesome!", task.getName());
        activitiRule.getTaskService().complete(task.getId());

        //发出一个自定义事件
        activitiRule.getRuntimeService()
            .dispatchEvent(new ActivitiActivityEventImpl(ActivitiEventType.CUSTOM));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CustomEventListener implements ActivitiEventListener {
    private static  final Logger logger= LoggerFactory.getLogger(CustomEventListener.class);

    @Override
    public void onEvent(ActivitiEvent event) {
        ActivitiEventType eventType = event.getType();
        //判断如果是自定义的事件
        if(ActivitiEventType.CUSTOM.equals(eventType)){
            logger.info("监听到自定义事件 {} \t {}",
                        event.getType(),event.getProcessInstanceId());
        }
    }

    @Override
    public boolean isFailOnException() {
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?
                                    useUnicode=true&amp;characterEncoding=utf8&amp;
                                    useSSL=false&amp;autoReconnect=true&amp;
                                    failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>
    <!--自定义监听事件-->
        <property name="eventListeners">
          <list>
            <bean class="com.guosh.activiti.event.CustomEventListener"/>
          </list>
        </property>
  </bean>
  <bean id="commandInvoker" class="com.guosh.activiti.interceptor.MDCCommandInvoker"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 8)通过代码注册监听

public class ConfigEventListenerTest {
    private static  final Logger logger= LoggerFactory
        .getLogger(ConfigEventListenerTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_evenlistener.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        activitiRule.getRuntimeService().addEventListener(new CustomEventListener());
        ProcessInstance processInstance = activitiRule
            .getRuntimeService()
            .startProcessInstanceByKey("my-process");
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        assertEquals("Activiti is awesome!", task.getName());
        activitiRule.getTaskService().complete(task.getId());
        //通过addEventListener注册
        activitiRule.getRuntimeService().addEventListener(new CustomEventListener());
        //发出一个自定义事件
        activitiRule.getRuntimeService()
            .dispatchEvent(new ActivitiActivityEventImpl(ActivitiEventType.CUSTOM));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 9)OnEvent源码

image.png

# 6、命令拦截器配置

# 1)命令模式与责任链模式

  • 命令模式:“四人帮”编写的经典的23种设计模式之一——行为模式

image.png

image.png

  • 责任链模式:多个拦截器组成的拦截器链

image.png

# 2)拦截器的配置方式

  • 配置Interceptor
    • customPreCommandInterceptors:配置在默认拦截器之前
    • customPostCommandInterceptors:配置在默认拦截器之后
    • commandInvoker:配置最后的执行器
public class DurationCommandInterceptor extends AbstractCommandInterceptor {

    private static final Logger logger = LoggerFactory
        .getLogger(DurationCommandInterceptor.class);

    @Override
    public <T> T execute(CommandConfig config, Command<T> command) {
        //取毫秒数
        long start =System.currentTimeMillis();
        try {
            return this.getNext().execute(config,command);
        }finally {
            long duration = System.currentTimeMillis() - start;
            logger.info("{} 执行时长 {} 毫秒",command.getClass().getSimpleName(),duration);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ConfigInterceptorTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigInterceptorTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_interceptor.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process.bpmn20.xml"})
    public void test() {
        ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");
        Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
        assertEquals("Activiti is awesome!", task.getName());
        activitiRule.getTaskService().complete(task.getId());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;autoReconnect=true&amp;failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>

    <!--拦截器-->
    <property name="customPreCommandInterceptors">
      <list>
        <bean class="com.guosh.activiti.interceptor.DurationCommandInterceptor"/>
      </list>
    </property>

    <property name="customPostCommandInterceptors">
      <list>
        <bean class="com.guosh.activiti.interceptor.DurationCommandInterceptor"/>
      </list>
    </property>

    <!--能实现mdc同样的效果-->
    <property name="enableVerboseExecutionTreeLogging" value="true"/>

    <!--自定义拦截每次执行流程都打印流程id-->
		<!--<property name="commandInvoker" ref="commandInvoker"/>-->
  </bean>
  <bean id="commandInvoker" class="com.guosh.activiti.interceptor.MDCCommandInvoker"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3)示例结果

image.png

image.png

# 4)MDCCommandInvoker

image.png

# 7、作业执行器配置

通过定时任务启动流程

# 1)作业执行器配置

  • 相关配置

    • asyncExecutorActivate:激活作业执行器
    • asyncExecutorXXX:异步执行器对属性配置(接口)
    • asyncExecutor:异步执行器bean
  • 自定义线程池 ExecutorService

    • threadNamePrefix:线程池名字前缀
    • corePoolSice:核心线程数
    • maxPoolSize:最大线程数
    • queueCapacity:堵塞队列大小
  • 定时开始事件 Timer Start Event

    • timDate:指定启动时间
    • timeDuration:指定持续时间间隔后执行
    • timCycle:R5/P1DT1H指定事件段后周期执行

# 2)配置自定义线程池

<?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="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?
                                    useUnicode=true&amp;characterEncoding=utf8&amp;
                                    useSSL=false&amp;autoReconnect=true&amp;
                                    failOverReadOnly=false" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <!--数据库更新策略-->
    <property name="databaseSchemaUpdate" value="true"/>

    <!--异步激活器-->
    <property name="asyncExecutorActivate" value="true"/>
    <!--异步执行器-->
    <property name="asyncExecutor" ref="asyncExecutor"/>
    <!--监听JOB事件-->
    <property name="eventListeners">
      <list>
        <bean class="com.guosh.activiti.event.JobEventListener"/>
      </list>
    </property>
  </bean>
  
  <bean id="asyncExecutor" class="org.activiti.engine.impl
                                  .asyncexecutor.DefaultAsyncJobExecutor">
    <property name="executorService" ref="executorService"/>
  </bean>
  <!--线程池-->
  <bean id="executorService" class="org.springframework.scheduling
                                    .concurrent.ThreadPoolExecutorFactoryBean">
    <!--线程池名字前缀-->
    <property name="threadNamePrefix" value="activiti-job"/>
    <!--核心线程池-->
    <property name="corePoolSize" value="5"/>
    <!--最大线程数-->
    <property name="maxPoolSize" value="20"/>
    <!--堵塞队列大小-->
    <property name="queueCapacity" value="100"/>
    <!--拒绝策略-->
    <property name="rejectedExecutionHandler">
      <bean class="java.util.concurrent.ThreadPoolExecutor$AbortPolicy"/>
    </property>

  </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 3)流程定时任务配置

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xmlns:activiti="http://activiti.org/bpmn"
    xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" 
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
    xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" 
             typeLanguage="http://www.w3.org/2001/XMLSchema"
    expressionLanguage="http://www.w3.org/1999/XPath" 
             targetNamespace="http://www.activiti.org/test">

    <process id="my-process">
        <!--<startEvent id="start" />-->
        <startEvent id="start" >
            <!--定时启动-->
            <timerEventDefinition>
                <!--含义执行五次间隔十秒-->
                <timeCycle>R5/PT10S</timeCycle>
            </timerEventDefinition>

        </startEvent>
        <sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />

        <userTask id="someTask" name="Activiti is awesome!" />
        <sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />

        <endEvent id="end" />
    </process>

</definitions>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 4)定时任务事件监听

public class JobEventListener implements ActivitiEventListener {
    private static  final Logger logger= LoggerFactory.getLogger(JobEventListener.class);

    @Override
    public void onEvent(ActivitiEvent event) {
        ActivitiEventType eventType = event.getType();
        String name=eventType.name();
        //判断如果是自定义的事件
        if(name.startsWith("TIMER")||name.startsWith("JOB")){
            logger.info("监听到JOB事件 {} \t {}",
                        event.getType(),event.getProcessInstanceId());
        }
    }

    @Override
    public boolean isFailOnException() {
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConfigJobTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigJobTest.class);

    @Rule
    public ActivitiRule activitiRule = new ActivitiRule("activiti_job.cfg.xml");

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process_job.bpmn20.xml"})
    public void test() throws InterruptedException {
        logger.info("start");
        //当前有多少个定时任务在执行
        List<Job> jobs = activitiRule.getManagementService().createTimerJobQuery().list();
        for (Job job:jobs) {
            logger.info("定时任务 = {},默认重试次数 = {}",job,job.getRetries());
        }
        logger.info("jobs.size = {}", jobs.size());
        //等待
        Thread.sleep(1000*100);

        logger.info("end");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5)示例结果

image.png

image.png

# 8、Activiti与Spring集成

  • 集成Spring配置
  • 基于Spring对Activiti管理
  • 基于Spring的流程单元测试

# 1)相关配置

  • 添加pom依赖activiti-spring
  • 基于spring的默认配置 activiti-context.xml (非 activiti.cfg.xml)
  • Activiti核心服务注入Spring容器

# 2)功能特征

  • 集成spring事物管理器
  • 定义文件表达式中使用Spring bean
  • 自动部署资源文件

# 3)单元测试

  • 添加pom依赖spring-test
  • 辅助测试Rule:**ActivitiRule **(推荐)
  • 辅助测试TestCase:SpringActrivitiTest(Unit 3)

# 4)配置示例

# ①配置activiti-context.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.spring.SpringProcessEngineConfiguration">

    <property name="databaseSchemaUpdate" value="true"/>
    <!--数据源-->
    <property name="dataSource" ref="dataSource"/>

    <!--事务管理器-->
    <property name="transactionManager" ref="transactionManager"/>
  </bean>

  <!--流程引擎对象-->
  <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
    <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
  </bean>

  <!--注册服务-->
  <bean id="runtimeService" 
        factory-bean="processEngine" factory-method="getRuntimeService"/>
  <bean id="repositoryService" 
        factory-bean="processEngine" factory-method="getRepositoryService"/>
  <bean id="formService" 
        factory-bean="processEngine" factory-method="getFormService"/>
  <bean id="taskService" 
        factory-bean="processEngine" factory-method="getTaskService"/>
  <bean id="historyService" 
        factory-bean="processEngine" factory-method="getHistoryService"/>

  <bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
    <property name="processEngine" ref="processEngine"/>
  </bean>

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/activiti6unit?
                                useUnicode=true&amp;characterEncoding=utf8&amp;
                                useSSL=false&amp;autoReconnect=true&amp;
                                failOverReadOnly=false"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="initialSize" value="2"/>
    <property name="maxActive" value="10"/>
  </bean>

  <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>
  
  <bean id="helloBean" class="com.guosh.activiti.delegate.HelloBean"></bean>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# ②测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:activiti-context.xml"})
public class ConfigSpringTest {
    private static  final Logger logger= LoggerFactory.getLogger(ConfigSpringTest.class);

    @Rule
    @Autowired
    public ActivitiRule activitiRule;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Test
    @Deployment(resources = {"com/guosh/activiti/my-process_spring.bpmn20.xml"})
    public void test() {
        ProcessInstance processInstance = runtimeService
            .startProcessInstanceByKey("my-process");
        assertNotNull(processInstance);
        Task task = taskService.createTaskQuery().singleResult();
        taskService.complete(task.getId());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# ③my-process_spring.bpmn20.xml流程文件

<?xml version="1.0" encoding="UTF-8"?>

<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
    xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
    xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
    expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">

    <process id="my-process">
        <startEvent id="start" />
        <sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />

        <userTask id="someTask" name="Activiti is awesome!" />
        <sequenceFlow id="flow2" sourceRef="someTask" targetRef="helloBean" />
        <!--${helloBean.sayHello()}可以调取到自己写到类到方法-->
        <serviceTask id="helloBean" activiti:expression="${helloBean.sayHello()}"/>
        <sequenceFlow id="flow3" sourceRef="helloBean" targetRef="end" />

        <endEvent id="end" />
    </process>

</definitions>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
编辑
#Activiti #Workflow
上次更新: 2020/06/11, 15:06:00
Activiti - 最成熟的JAVA工作流框架
Activiti使用手册(2)- Java API

← Activiti - 最成熟的JAVA工作流框架 Activiti使用手册(2)- Java API →

最近更新
01
Activiti使用手册(4)- Bpmn2规范
06-11
02
linux手动RPM安装gcc,g++
06-11
03
config - 通过@ConfigurationProperties读取yml配置
06-11
更多文章>
Theme by Vdoing | Copyright © 2020-2020 Leo Lee | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式