[TOC]
## 1.简介
Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。这个章节将用一个可以在你自己的开发环境中使用的例子,逐步介绍各种概念与API。
Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。 以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。 另外,也可以使用Flowable REST API进行HTTP调用。也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。
所有使用Flowable方法的共同点是核心引擎。核心引擎是一组服务的集合,并提供管理与执行业务流程的API。 下面的教程从设置与使用核心引擎的介绍开始。后续章节都建立在之前章节中获取的知识之上。
第一节展示了以最简单的方式运行Flowable的方法:只使用Java SE的标准Java main方法。这里也会介绍许多核心概念与API。
**另: Flowable是Activiti(Alfresco持有的注册商标)的fork。在下面的章节中,你会注意到包名,配置文件等等,都使用flowable。**
### 1.flowable的五个引擎
#### 1.1 flowable包含五个引擎,分别是:
1. 内容引擎 ContentEngine
1. 身份识别引擎 IdmEngine
1. 表单引擎 FormEngine
1. 决策引擎 DmnEngine
1. 流程引擎 ProcessEngine
#### 1.2 引擎包含的服务
每个引擎由相对应的 EngineConfiguration进行创建,在创建过程中对每个引擎使用的服务进行初始化。
##### 1.2.1 内容引擎 ContentEngine
**内容引擎包含的服务有: ContentManagementService**
>ContentManagementService提供对数据库表的管理操作,包括:
>
>Map<String, Long> getTableCount() 获取每个表的记录数量;
>String getTableName(Class<?> flowableEntityClass); 根据实体类获得对应的数据库表名;
>TableMetaData getTableMetaData(String tableName); 根据数据库表名获得表的列名和列类型;
>TablePageQuery createTablePageQuery(); 创建一个可以进行排序、根据条件分页的查询类。
>ContentService
>
>实现对内容的创建、删除、保存和获取的基本操作。
>
>ContentItem newContentItem();
>
>void saveContentItem(ContentItem contentItem);
>
>void saveContentItem(ContentItem contentItem, InputStream inputStream);
>
>InputStream getContentItemData(String contentItemId);
>
>void deleteContentItem(String contentItemId);
>
>void deleteContentItemsByProcessInstanceId(String processInstanceId);
>
>void deleteContentItemsByTaskId(String taskId);
>
>ContentItemQuery createContentItemQuery();
ContentEngineConfiguration
ContentEngineConfiguration最主要的作用是提供Mybatis的封装,实现数据库相关配置的获取。
同时,内容引擎配置还提供了操作系统级的文件操作的路径设置、文件读取、文件保存的功能。
##### 1.2.2 身份识别引擎 IdmEngine
**身份识别引擎包含的服务有:IdmIdentityService**
>提供用户的创建、修改、删除、密码修改、登录、用户头像设置等;
>提供组Group的创建、删除、用户与组关系的关联、删除关联;
>提供权限的创建、删除、关联等。
>
>IdmManagementService
>
>对身份识别相关的数据库表进行统计、获取表的列信息。
>
>IdmEngineConfiguration
>
>提供数据库配置信息。
##### 1.2.3 表单引擎 FormEngine
>表单引擎包含的服务有:
>
>FormManagementService
>FormRepositoryService
>FormService
>FormEngineConfiguration
##### 1.2.4 决策引擎 DmnEngine
>决策引擎包含的服务有:
>
>DmnManagementService
>DmnRepositoryService
>DmnRuleService
>DmnHistoryService
>DmnEngineConfiguration
##### 2.2.5 流程引擎 ProcessEngine
流程引擎包含的服务有:
> RepositoryService
> RuntimeService
> HistoryService
> IdentityService。
> TaskService。
> FormService。
> ManagementService。
>DynamicBpmnService
## 2.Flowable Api
### 2.1 流程引擎API与服务
引擎API是与Flowable交互的最常用手段。总入口点是ProcessEngine。ProcessEngine可以使用多种方式创建。使用ProcessEngine,可以获得各种提供工作流/BPM方法的服务。P**rocessEngine与服务对象都是线程安全的,因此可以在服务器中保存并共用同一个引用。**
![image](http://doc.miguren.cn/bpmn/images/api.services.png)
**流程引擎中的常用服务接口**
>提供一系列管理流程部署和流程定义的API。
**RepositoryService:**
RepositoryService repositoryService = processEngine.getRepositoryService();
>在流程运行时对流程实例进行管理与控制。
**RuntimeService**
RuntimeService runtimeService = processEngine.getRuntimeService();
>对流程的历史数据进行操作,包括查询、删除这些历史数据。
**HistoryService**
HistoryService historyService = processEngine.getHistoryService();
>提供对流程角色数据的API,这些角色数据包括用户组、用户及它们之间的关系。
**IdentityService**
IdentityService identityService = processEngine.getIdentityService();
> 对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
**TaskService**
TaskService taskService = processEngine.getTaskService();
> 表单服务。
**FormService**
FormService formService = processEngine.getFormService();
> 提供对流程引擎进行管理和维护的服务。
**ManagementService**
ManagementService managementService = processEngine.getManagementService();
>可用于修改流程定义中的部分内容,而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置,或者修改一个服务任务中的类名。
**DynamicBpmnService**
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
* * *
**RepositoryService很可能是使用Flowable引擎要用的第一个服务**。这个服务提供了管理与控制部署(deployments)与流程定义(process definitions)的操作。在这里简单说明一下,流程定义是BPMN 2.0流程对应的Java对象,体现流程中每一步的结构与行为。部署是Flowable引擎中的包装单元,一个部署中可以包含多个BPMN 2.0 XML文件及其他资源。开发者可以决定在一个部署中包含的内容,可以是单个流程的BPMN 2.0 XML文件,也可以包含多个流程及其相关资源(如’hr-processes’部署可以包含所有与人力资源流程相关的的东西)。RepositoryService可用于部署这样的包。部署意味着将它上传至引擎,引擎将在储存至数据库之前检查与分析所有的流程。在部署操作后,可以在系统中使用这个部署包,部署包中的所有流程都可以启动。
此外,这个服务还可以:
- 查询引擎现有的部署与流程定义。
- 暂停或激活部署中的某些流程,或整个部署。暂停意味着不能再对它进行操作,激活刚好相反,重新使它可以操作。
- 获取各种资源,比如部署中保存的文件,或者引擎自动生成的流程图。
- 获取POJO版本的流程定义。它可以用Java而不是XML的方式查看流程。
* * *
**RuntimeService用于启动流程定义的新流程实例**。前面介绍过,流程定义中定义了流程中不同步骤的结构与行为。流程实例则是流程定义的实际执行过程。同一时刻,一个流程定义通常有多个运行中的实例。RuntimeService也用于读取与存储流程变量。流程变量是流程实例中的数据,可以在流程的许多地方使用(例如排他网关经常使用流程变量判断流程下一步要走的路径)。RuntimeService还可以用于查询流程实例与执行(Execution)。执行也就是BPMN 2.0中 'token' 的概念。通常执行是指向流程实例当前位置的指针。最后,还可以在流程实例等待外部触发时使用RuntimeService,使流程可以继续运行。流程有许多等待状态(wait states),RuntimeService服务提供了许多操作用于“通知”流程实例:已经接收到外部触发,流程实例可以继续运行。
* * *
Flowable这样的BPM引擎来说,核心是需要人类用户操作的任务,**所有任务相关的东西都组织在TaskService中**,例如
* 查询分派给用户或组的任务
* 创建独立运行(standalone)任务。这是一种没有关联到流程实例的任务。
* 决定任务的执行用户(assignee),或者将用户通过某种方式与任务关联。
* 认领(claim)与完成(complete)任务。认领是指某人决定成为任务的执行用户,也即他将会完成这个任务。完成任务是指“做这个任务要求的工作”,通常是填写某个表单。
* * *
**IdentityService**,它用于管理(创建,更新,删除,查询……)组与用户。
Flowable实际上在运行时并不做任何用户检查。例如任务可以分派给任何用户,而引擎并不会验证系统中是否存在该用户。这是因为Flowable有时要与LDAP、Active Directory等服务结合使用。
****
**FormService是可选服务**。
也就是说Flowable没有它也能很好地运行,而不必牺牲任何功能。这个服务引入了开始表单(start form)与任务表单(task form)的概念。 开始表单是在流程实例启动前显示的表单,而任务表单是用户完成任务时显示的表单。Flowable可以在BPMN 2.0流程定义中定义这些表单。表单服务通过简单的方式暴露这些数据。再次重申,表单不一定要嵌入流程定义,因此这个服务是可选的。
* * *
**HistoryService** 暴露Flowable引擎收集的所有历史数据。当执行流程时,引擎会保存许多数据(可配置),例如流程实例启动时间、谁在执行哪个任务、完成任务花费的事件、每个流程实例的执行路径,等等。这个服务主要提供查询这些数据的能力。
* * *
**ManagementService**
通常在用Flowable编写用户应用时不需要使用。它可以读取数据库表与表原始数据的信息,也提供了对作业(job)的查询与管理操作。Flowable中很多地方都使用作业,例如定时器(timer),异步操作(asynchronous continuation),延时暂停/激活(delayed suspension/activation)等等。
* * *
**DynamicBpmnService**
可用于修改流程定义中的部分内容,而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置,或者修改一个服务任务中的类名。
### 2.2 异常策略
**Flowable的异常基类是org.flowable.engine.FlowableException**,这是一个非受检异常(unchecked exception)。在任何API操作时都可能会抛出这个异常,javadoc提供了每个方法可能抛出的异常。例如,从TaskService中摘录:
```
/**
* 当任务成功执行时调用。
* @param taskId 需要完成的任务id,不能为null。
* @throws FlowableObjectNotFoundException 若给定id找不到任务。
*/
void complete(String taskId);
```
在上例中,如果所用的id找不到任务,就会抛出异常。并且,**由于javadoc中明确要求taskId不能为null,因此如果传递了null值**,会抛出FlowableIllegalArgumentException异常。
尽管我们想避免过大的异常层次结构,但在特定情况下仍然会抛出下述异常子类。所有流程执行与API调用中发生的错误,如果不符合下面列出的异常,**会统一抛出FlowableExceptions**。
* FlowableWrongDbException: 当Flowable引擎检测到数据库表结构版本与引擎版本不匹配时抛出。
* FlowableOptimisticLockingException: 当对同一数据实体的并发访问导致数据存储发生乐观锁异常时抛出。、
* FlowableClassLoadingException: 当需要载入的类(如JavaDelegate, TaskListener, …)无法找到,或载入发生错误时抛出。
* FlowableObjectNotFoundException: 当请求或要操作的对象不存在时抛出。
* FlowableIllegalArgumentException: 当调用Flowable API时使用了不合法的参数时抛出。可能是引擎配置中的不合法值,或者是API调用传递的不合法参数,也可能是流程定义中的不合法值。
* FlowableTaskAlreadyClaimedException: 当对已被认领的任务调用taskService.claim(…)时抛出。
### 2.3 查询APi
从引擎中查询数据有两种方式:**查询API与原生(native)查询**。查询API可以使用链式API,通过编程方式进行类型安全的查询。你可以在查询中增加各种条件(所有条件都用做AND逻辑),也可以明确指定排序方式。下面是示例代码:
```
// 查询任务 受理人为kermit ,流程绑定参数为orderId- 0815的集合数据,并以日期排序
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
long count = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, " +
managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
.count();
```
### 2.4 变量
流程实例按步骤执行时,需要使用一些数据。在Flowable中,这些数据称作变量(variable),并会存储在数据库中。变量可以用在表达式中(**例如在排他网关中用于选择正确的出口路径**),也可以在Java服务任务(service task)中用于调用外部服务(例如为服务调用提供输入或结果存储),等等。
流程实例可以持有变量(称作流程变量 process variables);用户任务以及执行(executions)——流程当前活动节点的指针——也可以持有变量。流程实例可以持有任意数量的变量,每个变量存储为**ACT_RU_VARIABLE**数据库表的一行。
所有的startProcessInstanceXXX方法都有一个可选参数,用于在流程实例创建及启动时设置变量。例如,在RuntimeService中:
```
//启动流程实例, variables为传递的流程变量
ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
```
也可以在流程执行中加入变量。例如,(RuntimeService):
```
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
```
请注意可以为给定执行(**请记住,流程实例由一颗执行的树(tree of executions)组成**)设置局部(local)变量。局部变量将只在该执行中可见,对执行树的上层则不可见。这可以用于 数据不应该暴露给流程实例的其他执行,或者变量在流程实例的不同路径中有不同的值(例如使用并行路径时)的情况。
可以用下列方法读取变量。请注意TaskService中有类似的方法。这意味着任务与执行一样,可以持有局部变量,其生存期为任务持续的时间。
```
Map<String, Object> getVariables(String executionId);
Map<String, Object> getVariablesLocal(String executionId);
Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
Object getVariable(String executionId, String variableName);
<T> T getVariable(String executionId, String variableName, Class<T> variableClass);
```
变量通常用于Java代理(Java delegates)、表达式(expressions)、执行(execution)、任务监听器(tasklisteners)、脚本(scripts)等等。在这些结构中,提供了当前的execution或task对象,可用于变量的设置、读取。简单示例如下:
```
execution.getVariables();
execution.getVariables(Collection<String> variableNames);
execution.getVariable(String variableName);
execution.setVariables(Map<String, object> variables);
execution.setVariable(String variableName, Object value);
```
请注意也可以使用上例中方法的局部变量版本。
由于历史(与向后兼容)原因,当调用上述任何方法时,引擎会从数据库中取出所有变量。也就是说,如果你有10个变量,使用getVariable("myVariable")获取其中的一个,实际上其他9个变量也会从数据库取出并缓存。这并不坏,因为后续的调用可以不必再读取数据库。比如,如果流程定义包含三个连续的服务任务(因此它们在同一个数据库事务里),在第一个服务任务里通过一次调用获取全部变量,也许比在每个服务任务里分别获取需要的变量要好。请注意对读取与设置变量都是这样。
当然,如果使用大量变量,或者你希望精细控制数据库查询与流量,上述的做法就不合适了。我们引入了可以更精细控制的方法。这个方法有一个可选的参数,告诉引擎是否需要读取并缓存所有变量:
```
Map<String, Object> getVariables(Collection<String> variableNames, boolean fetchAllVariables);
Object getVariable(String variableName, boolean fetchAllVariables);
void setVariable(String variableName, Object value, boolean fetchAllVariables);
```
### 2.5 瞬时变量
### 2.5 任务监听listener
![a6c2efb2915d7ced1c84f5e3f2c6d76d.jpeg](evernotecid://D1CE3FA2-D07A-4091-9C4F-04C70ACE32BC/appyinxiangcom/29066271/ENResource/p10)
#### 2.5.1 任务监听器类型
* create:在任务创建且所有任务属性设置完成之后才触发。
* assignment:在任务被分配给某个班里人之后触发,它是在create事件触发前被触发。
* complete:在配置了监听器的任务完成时触发,也就是说运行期任务删除之前触发。
* delete:任务删除触发
#### 2.5.2 任务监听器的三种监听器执行类型
1. 类(class): :需要类的全路径
例子:
```
public class StartTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
logger.debug("调用了任务监听器");
}
}
```
任务监听的配置:
![c6d3fdd7d3b6a8baebb3159b29c6e0a8.png](evernotecid://D1CE3FA2-D07A-4091-9C4F-04C70ACE32BC/appyinxiangcom/29066271/ENResource/p11)
2.表达式(expression):定义一个表达式,类似EL的语法
![ddbc75a05afa142395a1e72be096d528.png](evernotecid://D1CE3FA2-D07A-4091-9C4F-04C70ACE32BC/appyinxiangcom/29066271/ENResource/p12)
3.委托表达式(delegateExpression):指的是一个实现监听接口
1.自定义接口 实现TaskListener, @Component 标识
```
@Component(value = "taskBusinessCallListener")
public class TaskBusinessCallListener extends BusinessCallListener implements TaskListener {
/**
* dubbo的类名
*/
private FixedValue clazzName;
/**
* 方法名
*/
private FixedValue method;
/**
* 版本号
*/
private FixedValue version;
/**
* 参数 多个的话用分号隔开 实例 userCode:00004737;status:1
*/
private FixedValue params;
@Override
public void notify(DelegateTask delegateTask) {
String processInstanceId = delegateTask.getProcessInstanceId();
//执行回调
this.callBack(processInstanceId, clazzName.getExpressionText(), method.getExpressionText(), version.getExpressionText(), params.getExpressionText());
}
}
```
![1a81a9ac921f605b37da59b94775e635.png](evernotecid://D1CE3FA2-D07A-4091-9C4F-04C70ACE32BC/appyinxiangcom/29066271/ENResource/p13)
* * *
* * *
* * *
* * *
* * *
flowable的办理方式,分为两种:签收模式办理和直接办理。
术语:
Assignee: 任务的受理人,即执行人。它有两种情况(有值,NULL)
Owner: 任务的委托人。
CandidateGroup: 候选用户组
CandidateUser: 候选人
delegateTask: 委派任务/签收的任务
resolveTask: 委派任务的代办,任务的拥有者把任务委派他人来办理,他人办完后,又重新回到任务拥有者,会产生流转记录。
turnTask: 转办任务,只是改变当前任务的办理人而已,不会产生流转记录。
CompleteTask: 完成任务,或叫办结提交下一步。
claimTask:任务签收