前段时间入职字节跳动, 目前负责 Lark 工作流审批功能的开发, 选用工作流引擎
Activiti
进行开发, 因此在此记录下对Activiti
的学习过程.
概念
工作流引擎是用来驱动业务, 按照流程图次逐步流转的核心框架, 在复杂多变的场景下采用工作流引擎可以大大降低业务部署成本. 通过标准的业务流程模型作为业务与开发工作的桥梁, 有效减少业务团队与技术交流的障碍.
工作流引擎最早用于企业 OA, CRM, 流程审批等系统的流程审批.
现在的工作流引擎已经大量运用到互联网电商, 金融出行, 中台支撑等.
工作流引擎在互联网公司快速盛行, 掌握工作流引擎技术可以提升技术架构和业务建模能力.
目录:
- 工作流入门
- Activiti 6.0 源码浅析
- Activiti 6.0 引擎配置
- Activiti 6.0 核心 API
- 数据设计与模型映射
- BPMN 2.0 规范
- 集成 SpringBoot 2.0
- 搭建工作流平台
1. 工作流入门
1.1 工作流介绍
1.1.1 出差流程
审批业务场景:
审批流程模型化:
从一个开始节点, 经过多个任务节点和分支节点, 最终流向结束节点.
1.1.2 电商购物流程
抽象成泳道图:
节点 | 抽象名称 | 功能 |
---|---|---|
电商购物流程 | 泳池(Pool) | |
用户/电商平台/仓储物流 | 泳道(Line) | |
校验库存 | 服务任务(Service Task) | 不需要人工参与, 需要系统自动化完成的操作节点 |
1.1.3 工作流是什么
工作流:
是对工作流程以及各个步骤之间的业务规则的抽象, 概括描述.
工作流建模:
将工作流程中的工作如何前后组织在一起的逻辑和规则, 在计算机中以恰当的模型表达并对其实施计算.
要解决的问题:
为实现某个业务目标, 利用计算机在多个参与者之间按某种预定规则自动传递文档, 信息或任务.
关键词 | 概念 |
---|---|
工作流管理系统 | 处理工作流的电脑软件系统, 主要功能是通过计算机技术的支持去定义, 执行和管理工作流, 协调工作流执行过程中工作之间以及群体之间的信息交互 |
计算机支持的协同工作 | 研究一个群体如何在计算机的帮助下实现协同工作的, 工作流属于计算机支持的协同工作的一部分 |
工作流管理联盟 | 工作流技术标准化的工业组织, 发布了用于工作流管理系统之间互操作的工作流参考模型, 并相继制定了一些列工业标准 |
1.1.4 为什么需要工作流
日常开发中经常遇到的问题:
- 产品需求遗漏, 开发上线之后需求经常改;
- 业务代码复杂, 开发时间紧迫;
- 代码后期维护不足, 逐渐难以维护.
使用工作流能够带来的改变:
- 可以快速响应, 灵活调整线上产品流程;
- 业务和开发基于流程模型沟通, 基于业务建模快速部署;
- 流程可视化, 方便查看流程的运行进展.
使用工作流对团队的作用:
- 提高效率, 减少等待;
- 规范行为, 落实制度;
- 协同内外, 快速响应;
- 监控全面, 提升执行.
1.2 工作流技术选型
二者都是成熟的工作流框架
jBPM | Activiti |
---|---|
Hibernate | ByBatis |
Drools Flow | JBPM4 |
JPA | Spring |
Message | RESTful |
1.3 Activiti6.0 快速体验
1.3.1 准备物料
- Activiti 软件包:
activiti-6.0.0.zip
- JDK
- Servlet 容器 (如 Tomcat)
安装 sdkman
1 | curl -s "https://get.sdkman.io" | bash |
安装 JDK
1 | sdk install java 8u161-oracle |
安装 Tomcat
1 | wget http://mirror.bit.edu.cn/apache/tomcat/tomcat/8/v8.0.50/bin/apache-tomcat-8.0.50.zip |
部署 Activiti
1 | wget https://github.com/Activiti/Activiti/releases/download/activiti-6.0.0/activiti-6.0.0.zip |
此时打开浏览器, 输入 http://localhost:8080/activiti-app 即可进入流程引擎的登录界面
账号: admin
密码: test
1.3.2 设计一个审批流程
设计如下流程:
开始 -> TL 审批 -> HR 审批 -> 结束
流程参与者
ID | Name | |
---|---|---|
admin | admin | Administrator |
userdev | userdev@126.com | userdevDEV |
userhr | userhr@126.com | userhrHR |
usertl | usertl@126.com | usertlTL |
2. 源码概述
1 | git clone git@github.com:DestinyWang/Activiti.git |
路径 | 功能 |
---|---|
Activiti/activiti-engine/src/main/java/org/activiti/engine/cfg |
Activiti 的启动依赖 activiti.cfg.xml , 在该目录完成 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/compatibility |
Activiti 从 5 升级到 6 的时候有部分不兼容, 在该目录完成适配 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/debug |
调试相关目录 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/delegate |
需要制定的节点 Task 都需要实现 JavaDelegate |
Activiti/activiti-engine/src/main/java/org/activiti/engine/event |
定义了事件和监听机制 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/form |
通用表单 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/history |
历史数据归档 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/identity |
身份认证相关操作 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/impl |
各个接口层的实现 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/logging |
LogMDC 将重要的变量(如流程 id 放在上下文, logback 可以打印出来) |
Activiti/activiti-engine/src/main/java/org/activiti/engine/management |
管理相关 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/parse |
流程文件是 xml, 需要解析和验证 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/query |
抽象了一些查询接口, 基于 mybatis |
Activiti/activiti-engine/src/main/java/org/activiti/engine/repository |
抽象流程部署到数据库的过程 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/runtime |
与 history 相对应, 是流程在流转过程中的数据 |
Activiti/activiti-engine/src/main/java/org/activiti/engine/task |
每个流程在需要人工处理的时候都会对应一个 task |
Activiti/activiti-engine/src/main/java/org/activiti/engine/test |
支持集成测试的帮助类 |
核心模块:
module/activiti-engine
: 核心引擎module/activiti-spring
: Spring 集成模块module/activiti-spring-boot
: SpringBoot 集成模块module/activiti-rest
: 对外提供 rest api 模块module/activiti-form-engine
: 表单引擎模块module/activiti-ldap
: 集成 ldap 用户模块
Activiti-engine 依赖的模块:
- bpmn-converter: 模型转换
- process-validation: 流程校验
- image-generator: 流程图绘制(BPMN 转 PNG)
- dmn-api: 决策标准
- form-api/form-model: form 表单相关
2.1 基于源码运行 activiti-app
2.1.1 启动 activiti-app
1 | cd modules/activiti-ui/activiti-app |
2.2 剖析 activiti-app
activiti-ui 的组成:
- activiti-app: 集成发布的 war 工程
- activiti-app-conf: UI 独立域业务外的配置
- activiti-app-logic: UI 的业务逻辑
- activiti-app-rest: 提供接口的 rest api
3. HelloWorld
- 填写审批信息: 姓名, 时间, 是否提交
- 主管审批: 审批结果, 备注
- 审批结果, 备注
3.1 在 IDEA 中完成流程图的设计并配置
配置点:
- 节点 id, 名称;
- 对每个网关的分支做判断(基于填写信息);
- Task 节点接收的表单信息.
3.1.2 Task 节点接收的表单信息
填写申请信息
Id | Name | Type | Expression | Variable | Default | Date Pattern | Readable | Writable | Required | Values |
---|---|---|---|---|---|---|---|---|---|---|
message | 申请信息 | string | True | |||||||
name | 申请人姓名 | string | True | |||||||
submitTime | 提交时间 | date | yyyy-MM-dd | True | ||||||
submitType | 确认申请 | string | True |
主管审批
Id | Name | Type | Expression | Variable | Default | Date Pattern | Readable | Writable | Required | Values |
---|---|---|---|---|---|---|---|---|---|---|
tlApprove | 主管审批结果 | string | false | |||||||
tlMessage | 主管审批备注 | string | true |
人事审批
Id | Name | Type | Expression | Variable | Default | Date Pattern | Readable | Writable | Required | Values |
---|---|---|---|---|---|---|---|---|---|---|
hrApprove | 人事审批结果 | string | true | |||||||
hrMessage | 人事审批备注 | string | true |
3.1.3 排他网关配置
排他网关需要对流入网关的某个值做判断, 从而决定流程后续的流向
flow3 配置
${submitType=="Y" || submitType=="y"}
flow4 配置
${submitType=="N" || submitType=="n"}
flow6 配置
${tlApprove == "Y" || tlApprove == "y"}
flow7 配置
${tlApprove == "N" || tlApprove == "n"}
flow9 配置
${hrApprove == "Y" || tlApprove == "y"}
flow10 配置
${hrApprove == "N" || tlApprove == "n"}
配置后的流程图
3.2 helloworld程序
1 | public class DemoMain { |
4. Activiti 引擎配置
4.1 流程引擎配置
流程引擎配置的载体就是 ProcessEngineConfiguration
及其子类, Activiti 是通过 activiti.cfg.xml
来完成配置
然后构建出流程引擎 ProcessEngine
, 最终获取业务开发中需要的各个 Service.
ProcessorEngineConfiguration:
- 查找并解析 XML 配置文件
activiti.cfg.xml
- 提供多个静态方法创建配置对象
- 实现几个基于不同场景的子类, 配置方式灵活
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
ProcessEngineConfigurationImpl: 抽象类, 配置了 ProcessEngineConfiguration 大部分属性;
StandaloneProcessEngineConfiguration: 独立部署运行, 可以通过 new 的方式创建;
SpringProcessEngineConfiguration: 完成与 Spring 的集成, 同时扩展了数据源配置, 事务, 自动装载部署文件的目录.
4.2 数据库配置
- 缺省配置默认使用 H2 内存数据库;
- 配置 JDBC 属性, 使用 MyBatis 提供的连接池;
- 配置 DataSource, 可自选第三方实现.
配置 JDBC 属性使用 MyBatis 提供的连接池
基本属性 | 连接池配置 |
---|---|
jdbcUrl | jdbcMaxActiveConnections(最大活跃连接数) |
jdbcDriver | jdbcMaxIdleConnections(最大空闲连接数) |
jdbcUsername | jdbcMaxCheckoutTime(最大) |
jdbcPassword | jdbcMaxWaitTIme(最大等待时间) |
配置第三方实现的 DataSource
- Druid: 为监控而生的数据库连接池
- Dbcp: Tomcat 自带
- HikariCP: 极速数据源连接池, Spring 默认
4.2.1 Druid 数据源连接池
1 | <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration"> |
4.2.2 数据库更新策略
- 配置 databaseSchemaUpdate:
- false: 启动时检查数据库版本, 发生不匹配则抛出异常(线上默认)
- true: 启动时自动检查并更新数据库表, 不存在会创建(开发环境默认)
- create-drop: 启动时创建数据库表结构, 结束时删除表结构
4.3 日志和数据记录配置
4.3.1 日志组件的关系及 MDC
日志分类 | 描述 | 分类内容 |
---|---|---|
日志门面 | 直接应用在程序中记录日志的组件 | slf4j, commons-logging, log4j |
日志实现 | 日志门面不能直接打日志, 需要日志实现 | logback, log4j, log4j2, Java util logging |
桥接方式 | 有些特殊需求, 例如需要 slf4j 作为门面, 但需要以 log4j 作为实现 | slf4j-log4j12, slf4j-jdk14, … |
改变依赖 | 将原有门面的功能委托给其他实现, 主要用于解决历史软件内部依赖的改变 | jcl-over-slf4j, log4j-over-slf4j, … |
配置开启 MDC(Mapped Diagnostic Context): 可以理解为将上下文数据存储在 ThreadLocal 中
- 默认没有开启, 需要手动设置
LogMDC.setMDCEnable(true)
- 配置 logback.xml, 日志模板添加
%X{mdcProcessInstanceID}
, 即打印当前 instance 的 id - 流程只有在执行过程出现异常的时候才会记录 MDC 信息
4.3.1.1 默认日志输出
测试类
1 | public class ConfigMDCTest { |
logback.xml
1 |
|
日志输出
00:19:43.624 [main] [INFO] Loading XML bean definitions from class path resource [activiti.cfg.xml] o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
00:19:45.162 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
00:19:45.174 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.mysql.create.engine.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:19:45.176 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:19:46.466 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.mysql.create.history.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:19:46.466 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:19:46.922 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.mysql.create.identity.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:19:46.922 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:19:47.035 [main] [INFO] ProcessEngine default created o.a.e.i.ProcessEngineImpl.<init>:87
4.3.1.2 MDC 日志输出
测试类
1 | public class ConfigMDCTest { |
BPMN 流程图
1 |
|
logback
1 |
|
日志输出
00:32:57.204 [main] [INFO] Loading XML bean definitions from class path resource [activiti.cfg.xml] ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
00:32:58.659 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
00:32:58.671 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.mysql.create.engine.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:32:58.673 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:33:00.219 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.mysql.create.history.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:33:00.220 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:33:00.657 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.mysql.create.identity.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:33:00.657 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:33:00.771 [main] [INFO] ProcessEngine default created ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.ProcessEngineImpl.<init>:87
00:33:00.932 [main] [INFO] MDCErrorDelegate.execute ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.d.MDCErrorDelegate.execute:24
00:33:00.935 [main] [ERROR] Error while closing command context ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.CommandContext.logException:122
java.lang.RuntimeException: test only
...
可以看到, 在 ERROR 行中, 打印出了 MDC 信息
4.3.1.3 使用拦截器让每个流程节点都把 MDC 信息打印出来
新建拦截器
1 | public class MDCCommandInvoker extends DebugCommandInvoker { |
配置该拦截器
修改默认配置文件 activiti.cfg.xml, 新增该 MDCCommandInvoker
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
最终产出日志
00:56:35.631 [main] [INFO] Loading XML bean definitions from class path resource [activiti_mdc.cfg.xml] ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
00:56:37.141 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
00:56:37.153 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.mysql.create.engine.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:56:37.154 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:56:38.655 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.mysql.create.history.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:56:38.656 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:56:39.109 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.mysql.create.identity.sql ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
00:56:39.110 [main] [INFO] Found MySQL: majorVersion=5 minorVersion=7 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.d.DbSqlSession.executeSchemaResource:1162
00:56:39.218 [main] [INFO] ProcessEngine default created ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.ProcessEngineImpl.<init>:87
00:56:39.403 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.407 [main] [INFO]
4 (process instance)
└── 5 : start (StartEvent, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.409 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.410 [main] [INFO]
4 (process instance)
└── 5 : start (StartEvent, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.411 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.412 [main] [INFO]
4 (process instance)
└── 5 : start -> someTask, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.412 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.412 [main] [INFO]
4 (process instance)
└── 5 : someTask (UserTask, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.497 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.501 [main] [INFO]
4 (process instance)
└── 5 : someTask (UserTask, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.502 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.503 [main] [INFO]
4 (process instance)
└── 5 : someTask (UserTask, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.504 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.504 [main] [INFO]
4 (process instance)
└── 5 : someTask -> end, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.505 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.505 [main] [INFO]
4 (process instance)
└── 5 : end (EndEvent, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.505 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.506 [main] [INFO]
4 (process instance)
└── 5 : end (EndEvent, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
00:56:39.507 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation : ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:33
00:56:39.507 [main] [INFO]
4 (process instance)
└── 5 : end (EndEvent, parent id 4 (active)
ProcessDefinitionId=my-process:1:3 executionId=5 mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.i.DebugCommandInvoker.executeOperation:34
可以看到最终所有级别的日志都输出了 MDC 信息
4.3.2 配置历史记录级别
配置 HistoryLevel:
- none: 不记录历史记录, 性能高, 流程结束后不可读取;
- activiti: 归档流程实例和活动实例, 流程变量不同步;
- audit: 默认值, 在 activiti 基础上同步变量值, 保存表单属性;
- full: 性能较差, 记录所有实例和变量细节变化.
4.3.3 配置基于 DB 的事件日志
配置 Event Logging
- 实验性的事件记录机制, 性能影响较大;
- 开启默认记录所有数据的变化过程, 导致表记录快速增长;
- 日志内容基于 JSON 格式, 建议存入 MongoDB, Elastic Search;
4.4 命令拦截器的配置
4.4.1 命令模式与责任链模式
4.4.1.1 Command
命令拦截器使用命令模式实现, 多个拦截器会组成一个拦截器链, 实现了责任链模式
- Command: 命令接口
- ConcreteCommand: 命令实现, 构造命令的时候, 需要传入接受者, 即 Received
- Receiver: Client 在实现 Command 接口的时候传入
- Invoker: 调用者, 最终调用 ConcreteCommand 对象
4.4.1.2 Chain of Responsibility
customPre, default, customPost 中
execute()
的实现基本都是调用了next
的execute()
, 只有 CommandInvoker 真正完成了执行器.
- CustomPre: default 之前的拦截器
- default: Activiti 默认的 CommandInterceptor
- CustomPost: default 之后的拦截器
- CommandInvoker: 最终的命令执行者
4.4.2 命令拦截器的配置
- 配置Interceptor
- customProCommandInterceptors: 配置在默认拦截器之前
- customPostCommandInterceptors: 配置在默认拦截器之后
- commandInvoker: 配置在最后的执行器
4.4.3 示例
需求
实现一个可以统计所有命令完成时间的拦截器
拦截器实现
1 | public class DurationCommandInterceptor extends AbstractCommandInterceptor { |
配置文件
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
日志输出
10:10:09.371 [main] [INFO] SchemaOperationsProcessEngineBuild 执行时长: 113 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.372 [main] [INFO] ProcessEngine default created ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.a.e.i.ProcessEngineImpl.<init>:87
10:10:09.391 [main] [INFO] ValidateExecutionRelatedEntityCountCfgCmd 执行时长: 14 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.394 [main] [INFO] 执行时长: 1 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.401 [main] [INFO] GetNextIdBlockCmd 执行时长: 4 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.508 [main] [INFO] GetProcessDefinitionInfoCmd 执行时长: 2 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.513 [main] [INFO] DeployCmd 执行时长: 116 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.616 [main] [INFO] CompleteTaskCmd 执行时长: 22 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
10:10:09.630 [main] [INFO] DeleteDeploymentCmd 执行时长: 14 毫秒 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.i.DurationCommandInterceptor.execute:33
4.5 作业执行器 Job Executor
4.5.1 作业执行器的配置
asyncExecutorActivate: 激活作业执行器
asyncExecutorXXX: 异步执行器的属性配置
asyncExecutor: 异步执行器 bean
定时开始事件
- timeDate: 指定启动时间
- timeDuration: 指定持续时间间隔后执行
- timeCycle:R5/P1DT1H 指定时间段后周期执行
4.5.2 配置自定义线程池
- corePoolSize: 核心线程数
- maxPoolSize: 最大线程数
- queueCapacity: 阻塞队列大小
如果核心线程数没满, 每当有一个任务, 不管原有线程是否空闲都会开启一个新的线程去执行, 直到达到核心线程数;
如果所有核心线程都在运行, 每当有一个任务, 会先放在阻塞队列等待, 直到核心线程执行完上一个任务, 会取阻塞队列第一个任务继续执行;
如果队列已满, 会继续创建新的线程, 直到达到最大线程数;
如果最大线程数和队列都已满, 此时会执行拒绝策略.
4.5.3 流程定义定时启动流程
4.5.4 Demo
配置异步执行器
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
流程定义文件
1 | <process id="my-process"> |
添加监听器
1 | public class JobEventListener implements ActivitiEventListener { |
日志输出
11:34:50.048 [main] [INFO] 监听 Job 事件: TIMER_SCHEDULED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:34:50.056 [main] [INFO] start ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.c.ConfigJobTest.test:33
11:34:50.080 [main] [INFO] 定时任务 TimerJobEntity [id=4], 默认重试次数: 3 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.c.ConfigJobTest.test:36
11:34:50.080 [main] [INFO] jobList.size: 1 ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.c.ConfigJobTest.test:38
11:35:09.981 [activiti-job-1] [INFO] 监听 Job 事件: TIMER_FIRED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:09.990 [activiti-job-1] [INFO] 监听 Job 事件: TIMER_SCHEDULED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:09.990 [activiti-job-1] [INFO] 监听 Job 事件: JOB_EXECUTION_SUCCESS null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:19.950 [activiti-job-2] [INFO] 监听 Job 事件: TIMER_FIRED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:19.951 [activiti-job-2] [INFO] 监听 Job 事件: TIMER_SCHEDULED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:19.951 [activiti-job-2] [INFO] 监听 Job 事件: JOB_EXECUTION_SUCCESS null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:29.958 [activiti-job-3] [INFO] 监听 Job 事件: TIMER_FIRED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:29.959 [activiti-job-3] [INFO] 监听 Job 事件: TIMER_SCHEDULED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:29.960 [activiti-job-3] [INFO] 监听 Job 事件: JOB_EXECUTION_SUCCESS null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:39.966 [activiti-job-4] [INFO] 监听 Job 事件: TIMER_FIRED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:39.970 [activiti-job-4] [INFO] 监听 Job 事件: TIMER_SCHEDULED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:39.971 [activiti-job-4] [INFO] 监听 Job 事件: JOB_EXECUTION_SUCCESS null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:49.975 [activiti-job-5] [INFO] 监听 Job 事件: TIMER_FIRED null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
11:35:49.975 [activiti-job-5] [INFO] 监听 Job 事件: JOB_EXECUTION_SUCCESS null ProcessDefinitionId= executionId= mdcProcessInstanceId= mdcBusinessKey= o.d.a.l.JobEventListener.onEvent:34
4.6 Activiti 与 Spring 集成
4.6.1 集成 Spring 配置
- 添加依赖:
1 | <dependency> |
- 基于 Spring 的默认配置:
activiti-context.xml
, 如果配置该文件, Activiti 在启动过程中就会查找基于 Spring 的ProcessEngineConfiguration
对象; - Activiti 核心服务注入 Spring 容器
4.6.2 基于 Spring 对 Activiti 的管理
4.6.2.1 集成 Spring 事务管理器
activiti-context.xml
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
4.6.2.2 定义文件表达式中使用 Spring Bean
HelloBean
1 | public class HelloBean { |
my-process-spring.bpmn20.xml
1 |
|
测试类
1 |
|
4.6.2.3 自动部署资源文件
4.6.3 基于 Spring 的流程单元测试
- 添加依赖
spring-test
- 辅助测试 Rule: ActivitiRule
- 辅助测试 TestCase: SpringActivitiTestCase
5. Activiti 核心 API
服务名称 | 功能 |
---|---|
RepositoryServie | 负责对静态文件的管理, 涉及部署对象和资源对象, 其二者是一对多的关系 |
RuntimeService | 负责对流程进行控制的服务, 可以对流程实例完成启动, 暂停, 挂起等操作 |
TaskService | 负责管理运行中的 UserTask(人工任务) |
IdentityService | 负责对用户和用户组的管理 |
FormService | 负责解析流程定义中的表单, 对表单的数据类型做渲染 |
HistoryService | 提供了对运行结束的流程实例的查询和删除操作 |
ManagementService | 提供了对流程引擎基础的管理, 提供对定时任务 Job 的管理, 获取表结构, 表明的操作 |
DynamicBpmnService | 提供了对动态的流程定义模型做修改 |
5.1 RepositoryService
- 管理流程定义文件 xml 及静态资源服务
- 对特定的流程的暂停和激活
- 流程定义启动权限管理
- 部署文件构造器
DeploymentBuilder
- 部署文件查询器
DeploymentQuery
- 流程定义文件查询对象
ProcessDefinitionQuery
- 流程部署文件对象
Deployment
- 流程定义文件对象
ProcessDefinition
- 流程定义的 Java 格式
BpmnModel
RepositoryService API:
方法名 | 功能 |
---|---|
createDeployment | 添加资源文件 |
deleteDeployment | 删除资源文件 |
setDeploymentCategory | 指定分类名称 |
createProcessDefinitionQuery | 创建流程定义查询对象 |
createNativeProcessDefinitionQuery | 通过 SQL 查询流程定义对象 |
suspendProcessDefinitionByXX | 通过某些条件暂停/挂起流程定义对象, 使之不能再生成新的流程实例 |
activateProcessDefinitionByXX | 通过某些条件激活流程定义对象, 使之可以继续生成新的流程实例 |
getProcssDiagram | 获取流程图的数据流 |
getBpmnModel | 获取 BpmnModel 对象 |
addCandidateStarterUser | 设置某个流程文件只能由指定的用户去启动 |
addCandidateStarterGroup | 设置某个流程文件只能由指定的用户组去启动 |
… | … |
5.1.1 ProcessDefinitionId 的含义
1 |
|
生成的日志:
08:42:45,507 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [activiti.cfg.xml]
08:42:47,193 [main] INFO org.activiti.engine.compatibility.DefaultActiviti5CompatibilityHandlerFactory - Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled.
08:42:47,207 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
08:42:47,268 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
08:42:47,274 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
08:42:47,280 [main] INFO org.activiti.engine.impl.ProcessEngineImpl - ProcessEngine default created
08:42:49,736 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - size of deploymentList: 2
08:42:49,736 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - deployment: DeploymentEntity[id=1, name=测试部署资源1]
08:42:49,736 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - deployment: DeploymentEntity[id=7, name=测试部署资源2]
08:42:49,742 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinition: ProcessDefinitionEntity[SecondApprove:1:5], version: 1, key: SecondApprove, name: 二级审批
08:42:49,742 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinition: ProcessDefinitionEntity[SecondApprove:2:11], version: 2, key: SecondApprove, name: 二级审批
08:42:49,742 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinition: ProcessDefinitionEntity[my-process:1:6], version: 1, key: my-process, name: null
08:42:49,743 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinition: ProcessDefinitionEntity[my-process:2:12], version: 2, key: my-process, name: null
两个 DeploymentEntity, 一个 id 为 1, 一个 id 为 7, id 的设置使用全局自增, 说明在两个 Deployment 对象的部署过程中插入了 6 条记录:
- 1 个部署记录;
- 2 个流程定义记录;
- 2 个 xml 文件对应的数据流记录;
- 1 个流程定义所生成的图片记录;(my-process 没有生成图片)
5.1.2 流程挂起/激活
测试代码:
1 |
|
输出日志:
09:12:42,071 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [activiti.cfg.xml]
09:12:43,614 [main] INFO org.activiti.engine.compatibility.DefaultActiviti5CompatibilityHandlerFactory - Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled.
09:12:43,627 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
09:12:43,682 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
09:12:43,688 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
09:12:43,692 [main] INFO org.activiti.engine.impl.ProcessEngineImpl - ProcessEngine default created
09:12:43,882 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinitionId: my-process:1:3
09:12:43,887 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - 开始启动
09:12:43,893 [main] ERROR org.activiti.engine.impl.interceptor.CommandContext - Error while closing command context
org.activiti.engine.ActivitiException: Cannot start process instance. Process definition null (id = my-process:1:3) is suspended
at org.activiti.engine.impl.util.ProcessInstanceHelper.createAndStartProcessInstance(ProcessInstanceHelper.java:67)
at org.activiti.engine.impl.util.ProcessInstanceHelper.createAndStartProcessInstance(ProcessInstanceHelper.java:51)
at org.activiti.engine.impl.cmd.StartProcessInstanceCmd.createAndStartProcessInstance(StartProcessInstanceCmd.java:109)
at org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:102)
at org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:37)
at org.activiti.engine.impl.interceptor.CommandInvoker$1.run(CommandInvoker.java:37)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:78)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:29)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
at org.activiti.engine.impl.RuntimeServiceImpl.startProcessInstanceById(RuntimeServiceImpl.java:114)
at org.destiny.activiti.coreapi.RepositoryServiceTest.testSuspend(RepositoryServiceTest.java:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.activiti.engine.test.ActivitiRule$1.evaluate(ActivitiRule.java:116)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
09:12:43,895 [main] ERROR org.destiny.activiti.coreapi.RepositoryServiceTest - 启动失败, 原因: Cannot start process instance. Process definition null (id = my-process:1:3) is suspended
09:12:43,897 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - 激活后开始启动
09:12:43,923 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - 激活后启动成功
5.1.3 绑定用户/用户组
测试代码:
1 | /** |
日志输出:
10:08:35,380 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [activiti.cfg.xml]
10:08:36,784 [main] INFO org.activiti.engine.compatibility.DefaultActiviti5CompatibilityHandlerFactory - Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled.
10:08:36,796 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
10:08:36,842 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
10:08:36,846 [main] INFO org.activiti.engine.impl.db.DbSqlSession - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
10:08:36,849 [main] INFO org.activiti.engine.impl.ProcessEngineImpl - ProcessEngine default created
10:08:37,009 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - processDefinitionId: my-process:1:3
10:08:37,016 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - 删除前: identityLink: [IdentityLinkEntity[id=4, type=candidate, userId=user, processDefId=my-process:1:3]]
10:08:37,016 [main] INFO org.destiny.activiti.coreapi.RepositoryServiceTest - 删除前: identityLink: [IdentityLinkEntity[id=5, type=candidate, groupId=groupM, processDefId=my-process:1:3]]
5.2 RuntimeService 流程运行控制服务
功能:
- 启动流程及对流程数据的控制
- 流程实例(ProcessInstance)与执行流(Execution)查询(当创建实例的时候, 一般也会创建一个执行流)
- 触发流程操作, 接受信号的消息
Runtime 启动流程及变量管理:
- 启动流程的常用方式(id, key, message)
- 启动流程可选参数:
- businessKey
- variables
- tenantId
- 变量(variables)的设置和获取
5.2.1 基本操作
5.2.1.1 根据流程定义 key 启动流程
每次流程部署时, 对应 ProcessDefintion 的 id 和 version 都会改变, 根据 ProcessDefintionKey 默认取最后一个版本的数据
1 |
|
5.2.1.2 根据流程定义 id
使用 ProcessDefintionId 进行获取的时候, 需要先通过
RepositoryService
获取到对应的 id
1 | /** |
5.2.1.3 通过 ProcessInstanceBuilder 完成流程的设置以及启动
1 |
|
5.2.1.4 设置和获取流程变量
1 |
|
日志输出:
11:55:06.844 [main] [INFO] variable map: {key1=value1, key2=value2, key3=newValue4, key4=value4} o.d.a.c.RuntimeServiceTest.testVariables:94
5.2.1.5 对流程实例的查询
1 |
|
5.2.1.6 对执行流的查询
1 |
|
5.2.2 流程实例与执行流
流程实例(ProcessInstance)
表示一次工作流业务的数据实体, 当每次启动流程的时候, 生成一个流程实例执行流(Execution)
表示流程实例中具体的执行路径, 如果简单的流程只有一条执行路径, 那么此时流程实例和执行流是一对一的关系- 流程实例接口继承与执行流
5.2.3 流程触发
- 使用 trigger 触发 receiveTask 节点
- 触发信号捕获事件 singalEventRecivied(信号可以全局发送)
- 触发消息捕获事件 messageEventReceived(消息只能针对某一个流程实例)
5.2.3.1 流程触发 trigger
流程配置文件
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
输出日志
1 | 14:59:58.256 [main] [INFO] execution: Execution[ id '5' ] - activity 'someTask - parent '4' o.d.a.c.RuntimeServiceTest.testTrigger:137 |
当完成了触发之后, 执行对象已经执行完成
5.2.3.2 流程触发 singalEventReceived
流程开始后, 流程会暂停在中间节点(SingalCatchingEvent), 当它获取到信号时间的时候, 才会继续流转
流程定义文件
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
日志输出
15:14:42.184 [main] [INFO] execution: Execution[ id '5' ] - activity 'signal-received - parent '4' o.d.a.c.RuntimeServiceTest.testSignalEventReceived:155
15:14:42.216 [main] [INFO] execution: null o.d.a.c.RuntimeServiceTest.testSignalEventReceived:163
5.2.3.3 流程触发 messageEventReceived
消息触发与信号触发非常相似, 唯一的不同是: 信号与具体的流程实例无关, 消息在执行过程中必须制定流程实例 id
流程定义文件
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
日志输出
15:37:52.024 [main] [INFO] execution: Execution[ id '5' ] - activity 'message-received - parent '4' o.d.a.c.RuntimeServiceTest.testMessageEventReceived:175
15:37:52.054 [main] [INFO] execution: null o.d.a.c.RuntimeServiceTest.testMessageEventReceived:183
5.2.3.4 流程基于 message 启动
流程定义
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
日志输出
15:45:12.844 [main] [INFO] processInstance: ProcessInstance[5] o.d.a.c.RuntimeServiceTest.testMessageStart:195
基于 message 启动流程时, ProcessInstance 的 id 是 5, 意味着在流程订阅表中多插入了一条数据, 在实际启动过程中, 还是通过 message 找到 ProcessDefinition 的 key, 最终根据 key 来启动
5.3 TaskService 任务管理服务
TaskService 提供的功能:
- 对用户任务管理和流程的控制
- 设置用户任务的权限信息(拥有者/候选人/办理人)
- 针对用户任务添加任务附件, 任务评论和事件记录
TaskService 对 Task 管理与流程控制:
- Task 对象的创建, 删除
- 查询 Task, 并驱动 Task 节点完成执行
- Task 相关参数变量设置
- local 变量
- 非 local 变量
5.3.1 基本操作
5.3.1.1 获取 task/ 设置变量/ 驱动完成
流程定义文件
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
日志输出
16:13:26.654 [main] [INFO] Loading XML bean definitions from class path resource [activiti.cfg.xml] o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
16:13:28.438 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
16:13:28.451 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:13:28.527 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:13:28.535 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:13:28.541 [main] [INFO] ProcessEngine default created o.a.e.i.ProcessEngineImpl.<init>:87
16:13:28.793 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.796 [main] [INFO]
4 (process instance)
└── 6 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.798 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.799 [main] [INFO]
4 (process instance)
└── 6 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.800 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.800 [main] [INFO]
4 (process instance)
└── 6 : start -> someTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.802 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.802 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.878 [main] [INFO] task: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Activiti is awesome!","localizedName":null,"description":"some task my test message","localizedDescription":null,"priority":50,"createTime":"Mon Dec 03 16:13:28 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"6","execution":null,"processInstanceId":"4","processInstance":null,"processDefinitionId":"my-process:1:3","taskDefinitionKey":"someTask","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"9","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskService:33
16:13:28.878 [main] [INFO] task.description: some task my test message o.d.a.c.TaskServiceTest.testTaskService:34
16:13:28.893 [main] [INFO] taskServiceVariables: {k1=v1, localK1=localV1, message=my test message} o.d.a.c.TaskServiceTest.testTaskService:46
16:13:28.893 [main] [INFO] taskServiceVariablesLocal: {localK1=localV1} o.d.a.c.TaskServiceTest.testTaskService:47
16:13:28.893 [main] [INFO] processVariables: {k1=v1, message=my test message} o.d.a.c.TaskServiceTest.testTaskService:48
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : someTask -> end, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:13:28.893 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:13:28.893 [main] [INFO] task1: null o.d.a.c.TaskServiceTest.testTaskService:55
5.3.1.2 TaskService 设置 Task 权限信息
- 候选用户(candidateUser) 和候选组(candidateGroup)
- 指定拥有人(Owner)和办理人(Assignee)
- 通过 claim 设置办理人(发现 task 已经有指定办理人且不是 claim 指定的人就会抛出异常)
流程定义
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
输出日志
16:41:28.534 [main] [INFO] Loading XML bean definitions from class path resource [activiti.cfg.xml] o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
16:41:30.373 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
16:41:30.385 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:41:30.443 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:41:30.449 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
16:41:30.453 [main] [INFO] ProcessEngine default created o.a.e.i.ProcessEngineImpl.<init>:87
16:41:30.631 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.636 [main] [INFO]
4 (process instance)
└── 6 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.638 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.638 [main] [INFO]
4 (process instance)
└── 6 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.639 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.640 [main] [INFO]
4 (process instance)
└── 6 : start -> someTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.640 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.641 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.715 [main] [INFO] task: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Activiti is awesome!","localizedName":null,"description":"some task my test message","localizedDescription":null,"priority":50,"createTime":"Mon Dec 03 16:41:30 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"6","execution":null,"processInstanceId":"4","processInstance":null,"processDefinitionId":"my-process:1:3","taskDefinitionKey":"someTask","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"9","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskServiceUser:69
16:41:30.715 [main] [INFO] task.description: some task my test message o.d.a.c.TaskServiceTest.testTaskServiceUser:70
16:41:30.739 [main] [INFO] identityLink: IdentityLinkEntity[id=10, type=candidate, userId=destiny, taskId=9] o.d.a.c.TaskServiceTest.testTaskServiceUser:94
16:41:30.740 [main] [INFO] identityLink: IdentityLinkEntity[id=12, type=candidate, userId=destiny1, taskId=9] o.d.a.c.TaskServiceTest.testTaskServiceUser:94
16:41:30.740 [main] [INFO] identityLink: IdentityLinkEntity[id=14, type=candidate, userId=destiny2, taskId=9] o.d.a.c.TaskServiceTest.testTaskServiceUser:94
16:41:30.740 [main] [INFO] identityLink: IdentityLinkEntity[id=null, type=assignee, userId=destiny, taskId=9] o.d.a.c.TaskServiceTest.testTaskServiceUser:94
16:41:30.740 [main] [INFO] identityLink: IdentityLinkEntity[id=null, type=owner, userId=user1, taskId=9] o.d.a.c.TaskServiceTest.testTaskServiceUser:94
16:41:30.749 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.751 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.752 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.753 [main] [INFO]
4 (process instance)
└── 6 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.754 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.754 [main] [INFO]
4 (process instance)
└── 6 : someTask -> end, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.755 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.755 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.755 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.755 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.757 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
16:41:30.757 [main] [INFO]
4 (process instance)
└── 6 : end (EndEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
16:41:30.770 [main] [INFO] 是否存在: true o.d.a.c.TaskServiceTest.testTaskServiceUser:106
5.3.2 TaskService 设置 Task 附加信息
- 任务附件(Attachment)创建与查询
- 任务评论(Comment)创建与查询
- 事件记录(Event)创建于查询
5.3.2.1 任务附件(Attachment)创建与查询
流程定义
同上
测试代码
1 |
|
日志输出
17:12:47.043 [main] [INFO] attachment: org.activiti.engine.impl.persistence.entity.AttachmentEntityImpl@72b16078 o.d.a.c.TaskServiceTest.testTaskAttachment:119
17:12:47.050 [main] [INFO] taskAttachment: {"name":"name","description":"desc","type":"url","taskId":"9","processInstanceId":"4","url":"/url/test.png","contentId":null,"content":null,"userId":null,"time":"Mon Dec 03 17:12:47 CST 2018","id":"16","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskAttachment:122
5.3.2.2 任务评论(Comment)创建与查询
流程定义
同上
测试代码
1 |
|
日志输出
17:05:17.107 [main] [INFO] taskComment: {"type":"comment","userId":null,"time":"Mon Dec 03 17:05:17 CST 2018","taskId":"9","processInstanceId":"4","action":"AddComment","message":"record note2","fullMessage":"record note2","id":"17","isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskComment:140
17:05:17.107 [main] [INFO] taskComment: {"type":"comment","userId":null,"time":"Mon Dec 03 17:05:17 CST 2018","taskId":"9","processInstanceId":"4","action":"AddComment","message":"record note1","fullMessage":"record note1","id":"16","isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskComment:140
17:05:17.109 [main] [INFO] taskEvent: {"type":"comment","userId":null,"time":"Mon Dec 03 17:05:17 CST 2018","taskId":"9","processInstanceId":"4","action":"AddComment","message":"record note2","fullMessage":"record note2","id":"17","isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskComment:146
17:05:17.110 [main] [INFO] taskEvent: {"type":"comment","userId":null,"time":"Mon Dec 03 17:05:17 CST 2018","taskId":"9","processInstanceId":"4","action":"AddComment","message":"record note1","fullMessage":"record note1","id":"16","isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.TaskServiceTest.testTaskComment:146
Comment 和 Event 的区别
所有对 task 的操作都会生成新的 Event 记录, comment 只是其中的一种, 比如在上例中新增 owner 或 assignee, 也会产生新的 event 记录
5.4 身份管理服务
Activiti 提供了相对比较简单的用户/用户组管理
主要功能:
- 管理用户(User)
- 管理用户组(Group)
- 用户与用户组的关系(Membership)(多对多关系)
5.4.1 创建用户/用户组/对应关系
流程定义
此处不需要基于流程定义完成
测试代码
1 |
|
日志输出
17:37:12.982 [main] [INFO] Loading XML bean definitions from class path resource [activiti.cfg.xml] o.s.b.f.x.XmlBeanDefinitionReader.loadBeanDefinitions:316
17:37:14.894 [main] [INFO] Activiti 5 compatibility handler implementation not found or error during instantiation : org.activiti.compatibility.DefaultActiviti5CompatibilityHandler. Activiti 5 backwards compatibility disabled. o.a.e.c.DefaultActiviti5CompatibilityHandlerFactory.createActiviti5CompatibilityHandler:38
17:37:14.910 [main] [INFO] performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
17:37:14.971 [main] [INFO] performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
17:37:14.978 [main] [INFO] performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql o.a.e.i.d.DbSqlSession.executeSchemaResource:1147
17:37:14.982 [main] [INFO] ProcessEngine default created o.a.e.i.ProcessEngineImpl.<init>:87
17:37:15.041 [main] [INFO] user: {"firstName":null,"lastName":null,"email":"destinywk@163.com","password":null,"pictureByteArrayRef":"ByteArrayRef[id=null, name=null, entity=null]","id":"user1","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.IdentityServiceTest.testIdentity:46
17:37:15.042 [main] [INFO] user: {"firstName":null,"lastName":null,"email":"destinywk@126.com","password":null,"pictureByteArrayRef":"ByteArrayRef[id=null, name=null, entity=null]","id":"user2","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.IdentityServiceTest.testIdentity:46
17:37:15.048 [main] [INFO] group: {"name":null,"type":null,"id":"group1","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.IdentityServiceTest.testIdentity:52
17:37:15.048 [main] [INFO] group: {"name":null,"type":null,"id":"group2","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false} o.d.a.c.IdentityServiceTest.testIdentity:52
身份管理服务的调用过程:
会将 User 封装成一个 Command, 交由命令执行器去执行, 最后调用 MyBatis 底层接口去操作 DB
5.5 表单管理服务 FormService
功能:
- 解析流程定义中表单项的配置
- 提供提交表单的方式驱动用户节点流转
- 获取自定义外部表单 key
5.5.1
流程定义
1 | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
测试代码
1 |
|
日志输出
18:15:27.539 [main] [INFO] startFormKey: /rest/process/form/start o.d.a.c.FormServiceTest.testFormService:36
18:15:27.547 [main] [INFO] startFormKey: {"processDefinition":"ProcessDefinitionEntity[my-process:1:3]","formKey":"/rest/process/form/start","deploymentId":"1","formProperties":[org.activiti.engine.impl.form.FormPropertyImpl@3bde62ff]} o.d.a.c.FormServiceTest.testFormService:38
18:15:27.547 [main] [INFO] startFormProperty: {"id":"message","name":"信息","type":"org.activiti.engine.impl.form.StringFormType@2baa8d82","isRequired":true,"isReadable":true,"isWritable":true,"value":null} o.d.a.c.FormServiceTest.testFormService:40
18:15:27.561 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
18:15:27.564 [main] [INFO]
4 (process instance)
└── 5 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
18:15:27.565 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
18:15:27.566 [main] [INFO]
4 (process instance)
└── 5 : start (StartEvent, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
18:15:27.567 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
18:15:27.567 [main] [INFO]
4 (process instance)
└── 5 : start -> someTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
18:15:27.568 [main] [INFO] Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation : o.a.e.i.i.DebugCommandInvoker.executeOperation:33
18:15:27.568 [main] [INFO]
4 (process instance)
└── 5 : someTask (UserTask, parent id 4 (active)
o.a.e.i.i.DebugCommandInvoker.executeOperation:34
18:15:27.602 [main] [INFO] taskFormData: {"task":"Task[id=10, name=Activiti is awesome!]","formKey":"/rest/process/form/userTask","deploymentId":"1","formProperties":[org.activiti.engine.impl.form.FormPropertyImpl@4e4efc1b]} o.d.a.c.FormServiceTest.testFormService:53
18:15:27.602 [main] [INFO] taskFormProperty: {"id":"yesOrNo","name":"审批","type":"org.activiti.engine.impl.form.StringFormType@2baa8d82","isRequired":true,"isReadable":true,"isWritable":true,"value":null} o.d.a.c.FormServiceTest.testFormService:55
5.6 HistoryService 历史数据管理服务
作用:
- 管理流程实例哦结束后的历史数据
- 构建历史数据的查询对象
- 根据流程实例 id 删除流程历史数据
历史数据实体 | 描述 |
---|---|
HistoricProcessInstance | 历史流程实例实体类 |
HistoricVariableInstance | 流程或任务变量值的实体 |
HistoricActivityInstance | 单个活动节点执行的信息 |
HistoricTaskInstance | 用户任务实例的信息 |
HistoricDetail | 历史流程活动任务详细信息 |
- HistoryService 构建历史查询对象:
- create
[历史数据实体]
Query - createNative
[历史数据实体]
Query - createProcessInstanceHistoryLogQuery: 只能查出一个流程实例的一个对象, 每次只能查出一条记录, 包含流程实体所有的其他数据, 包括task, Activiti, comment 等信息
- create
- HistoryService 删除历史操作
- deleteHistoricProcessInstance, 采用级联操作, 删除与流程实例相关的所有历史信息
- deleteHistoricTaskInstance, 范围相对较小, 只删除 Task 及 Task 相关的变量
5.7 ManagementService 管理服务
作用:
- Job 任务管理
- 数据库相关通用操作
- 执行流程引擎命令(Command)
5.7.1 Job 任务管理
工作查询对象 | 描述 |
---|---|
JobQuery | 查询一般工作 |
TimerJobQuery | 查询定时任务 |
SuspendedKobQUery | 查询中断工作 |
DeadLetterJobQuery | 查询无法执行的工作(一般重试三次) |
5.7.2 数据库相关操作
- 查询表结构元数据
- 通用表查询
- 执行自定义的 sql 查询
5.8 DynamicBpmnService 动态流程定义服务
不推荐使用
6 数据库设计和模型映射
数据表分类 | 描述 |
---|---|
ACT_GE_* |
通用数据表 |
ACT_RE_* |
流程定义存储表 |
ACT_ID_* |
身份信息表 |
ACT_RU_* |
运行时数据库表 |
ACT_HI_* |
历史数据库表 |
6.1 MySql 建表语句
除了核心引擎是必选的, 其他两个不是必须的
- 核心引擎: activiti.mysql.create.engine.sql
- 历史数据: activiti.mysql.create.history.sql
- 身份信息: activiti.mysql.create.identity.sql
6.1.1 通用数据库
数据表分类 | 描述 |
---|---|
ACT_GE_PROPERTY | 属性表(保存流程引擎的 key-value 键值属性) |
ACT_GE_BYTEARRAY | 资源表(存储流程定义相关的资源) |
7 BPMN2.0 概述
- 是一套业务流程模型与符号建模标准
- 精准的执行语义来描述元素的操作
- 以 XML 为载体, 以符号可视化业务
BPMN2.0元素:
- 流对象
- 连接对象
- 数据
- 泳道
- 描述对象
7.1 流对象
- 活动(Activities): User Task, Service Task…
- 事件(Events): Start Event, End Event…
- 网关(Gateway): Exclusive Gateway…
8 Activiti 集成 Spring Boot
Activiti6.0 依赖的 SpringBoot 版本是 1.2.6
如果直接与 SpringBoot 2.0.0 集成的话, 会出现 ClassNotFound 等问题
因此在集成 SpringBoot 2.0.0 的时候, 需要 Activiti6.0 源码进行部分改动
升级 Activiti 6.0 依赖 SpringBoot 版本为 2.0.0 的改动
- 升级 SpringBoot 依赖并解决编译错误
- 更新 activiti-spring-boot-starter-basic 版本并安装
- 集成使用 Activiti 的 AutoConfiguration 功能
如果直接将 SpringBoot 2.0.0 和 activiti-spring-boot-starter-basic 6.0.0 集成, 会发生如下错误:
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'documentationPluginsBootstrapper' defined in URL [jar:file:/Users/destiny/.m2/repository/io/springfox/springfox-spring-web/2.8.0/springfox-spring-web-2.8.0.jar!/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL [jar:file:/Users/destiny/.m2/repository/io/springfox/springfox-spring-web/2.8.0/springfox-spring-web-2.8.0.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL [jar:file:/Users/destiny/.m2/repository/io/springfox/springfox-spring-web/2.8.0/springfox-spring-web-2.8.0.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
...
Caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
...
真正的原因是对应的类无法找到
8.1 修改 activiti 源码以适应 SpringBoot 2.0.0 版本升级
8.1.1 将 activiti 中 SpringBoot 依赖升级到 2.0.0
修改 Activiti 源码中 modules/activiti-spring-boot/pom.xml
pom 文件, 将其中
1 | <spring.boot.version>1.2.6.RELEASE</spring.boot.version> |
修改为
1 | <spring.boot.version>2.0.0.RELEASE</spring.boot.version> |
然后重新编译源码, 此时以下几个类会报错
8.1.1.1 org.activiti.spring.boot.actuate.endpoint.ProcessEngineEndpoint
主要的问题是 SpringBoot 从 1 升级到 2 的时候, 对 EndPoint 的使用方式发生了改变:
1.x 是继承一个抽象类 AbstractEndpoint
2.x 修改为使用对应的注解
修改后的源码如下:
1 | package org.activiti.spring.boot.actuate.endpoint; |
8.1.1.2 org.activiti.spring.boot.actuate.endpoint.ProcessEngineMvcEndpoint
1 | package org.activiti.spring.boot.actuate.endpoint; |
8.1.1.3 org.activiti.spring.boot.EndpointAutoConfiguration
报错原因: SpringBoot2 不再使用 AbstractEndpoint
开启该表达式 AutoConfiguration 不会生效, 因此需要删除
1 | package org.activiti.spring.boot; |
8.1.1.4 org.activiti.spring.boot.DataSourceProcessEngineAutoConfiguration
报错原因: ConditionalOnMissingClass 注解中的 name 已经不能使用
1 | package org.activiti.spring.boot; |
8.1.1.4 org.activiti.spring.boot.SecurityAutoConfiguration
报错原因: 包结构发生改变, 导致原有的类全路径不可用
1 | import org.activiti.engine.IdentityService; |
8.2 对 Activiti 版本做更改
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
尝试执行 mvn clean install
此时 maven 会去尝试下载 6.0.0-boot2
版本, 但显然公网仓库中不会存在
[INFO] Scanning for projects...
[INFO]
[INFO] ----------< org.activiti:activiti-spring-boot-starter-basic >-----------
[INFO] Building activiti-spring-boot-starter-basic 6.0.0-boot2
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The POM for org.activiti:activiti-engine:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-spring:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-rest:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-common-rest:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-image-generator:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-bpmn-model:jar:6.0.0-boot2 is missing, no dependency information available
[WARNING] The POM for org.activiti:activiti-bpmn-layout:jar:6.0.0-boot2 is missing, no dependency information available
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.850 s
[INFO] Finished at: 2018-12-03T23:15:57+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project activiti-spring-boot-starter-basic: Could not resolve dependencies for project org.activiti:activiti-spring-boot-starter-basic:jar:6.0.0-boot2: The following artifacts could not be resolved: org.activiti:activiti-engine:jar:6.0.0-boot2, org.activiti:activiti-spring:jar:6.0.0-boot2, org.activiti:activiti-rest:jar:6.0.0-boot2, org.activiti:activiti-common-rest:jar:6.0.0-boot2, org.activiti:activiti-image-generator:jar:6.0.0-boot2, org.activiti:activiti-bpmn-model:jar:6.0.0-boot2, org.activiti:activiti-bpmn-layout:jar:6.0.0-boot2: Failure to find org.activiti:activiti-engine:jar:6.0.0-boot2 in http://maven.aliyun.com/nexus/content/groups/public was cached in the local repository, resolution will not be reattempted until the update interval of nexus-aliyun has elapsed or updates are forced -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
错误原因: activiti-spring-boot-starter-basic 的版本号被修改为 6.0.0-boot2, 但安装的时候, 对应的
activiti-engine
版本只有 6.0.0, 并没有6.0.0-boot2
可以将所有需要找6.0.0-boot2
的版本修改为去找6.0.0
将根 pom 中所有的 ${project.version}
修改为 6.0.0