Flowable 简介
1. 概述
Flowable 是一个用 Java 编写的业务流程引擎。在本教程中,我们将详细介绍业务流程并了解如何利用 Flowable Java API 创建和部署示例业务流程。
2. 了解业务流程
简而言之,业务流程是一组任务,一旦按照定义的顺序完成,就可以实现定义的目标。业务流程中的每个任务都有明确定义的输入和输出。这些任务可能需要人工干预或完全自动化。
OMG(对象管理组)定义了一个称为业务流程模型和表示法 (BPMN) 的标准,供企业定义和交流他们的流程。BPMN 已在业界得到广泛支持和接受。Flowable API 完全支持创建和部署 BPMN 2.0 流程定义。
3. 创建流程定义
假设我们在发表之前有一个简单的文章审查流程。
这个过程的要点是作者提交一篇文章,编辑要么接受要么拒绝它。如果被接受,文章立即发表;但是,如果被拒绝,则会通过电子邮件通知作者:
我们使用 BPMN 2.0 XML 标准将流程定义创建为 XML 文件。
让我们根据 BPMN 2.0 标准定义我们的简单流程:
<?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:xsd="http://www.w3.org/2001/XMLSchema"
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"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="articleReview"
name="A simple process for article review." isExecutable="true">
<startEvent id="start" />
<sequenceFlow sourceRef="start" targetRef="reviewArticle" />
<userTask id="reviewArticle" name="Review the submitted tutorial"
flowable:candidateGroups="editors" />
<sequenceFlow sourceRef="reviewArticle" targetRef="decision" />
<exclusiveGateway id="decision" />
<sequenceFlow sourceRef="decision" targetRef="tutorialApproved">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${approved}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="tutorialRejected">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${!approved}]]>
</conditionExpression>
</sequenceFlow>
<serviceTask id="tutorialApproved" name="Publish the approved tutorial."
flowable:class="com.blogdemo.service.PublishArticleService" />
<sequenceFlow sourceRef="tutorialApproved" targetRef="end" />
<serviceTask id="tutorialRejected" name="Send out rejection email"
flowable:class="com.blogdemo.service.SendMailService" />
<sequenceFlow sourceRef="tutorialRejected" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
现在,这里有相当多的元素是标准 XML 内容,而其他元素则特定于 BPMN 2.0:
- 整个**过程被包裹在一个名为“process”的标签中,**而这个标签又是一个名为“definitions”的标签的一部分
- 流程由事件、流、任务和网关组成
- 事件是开始事件或结束事件
- 流(在本例中为序列流)连接其他元素,如事件和任务
- 任务是完成实际工作的地方;这些可以是“用户任务”或“服务任务”等
- 用户任务需要人类用户与 Flowable API 交互并采取行动
- 服务任务代表一个自动任务,它可以是对 Java 类的调用,甚至是 HTTP 调用
- 网关基于“已批准”属性执行;这被称为流程变量,我们稍后会看到如何设置它们
虽然我们可以在任何文本编辑器中创建流程定义文件,但这并不总是最方便的方式。不过幸运的是,Flowable 还提供了用户界面选项,可以使用Eclipse 插件 或Web 应用程序 来实现这一点。如果您改用 IntelliJ,也可以使用IntelliJ 插件 。
4. 使用 Flowable API
现在我们已经按照 BPMN 2.0 标准在 XML 文件中定义了我们的简单流程,我们需要一种方法来提交和运行它。Flowable 提供了 Process Engine API 来与 Flowable Engines 交互。Flowable 非常灵活,并提供了多种部署此 API 的方法。
鉴于 Flowable 是一个 Java API,我们可以通过简单地包含必要的 JAR 文件将流程引擎包含在任何 Java 应用程序中。我们可以很好地利用 Maven 来管理这些依赖项。
此外,Flowable 附带捆绑的 API 以通过 HTTP 与 Flowable 进行交互。我们可以通过 Flowable API 使用这些 API 来做任何其他可能的事情。
最后,**Flowable 对与 Spring 和 Spring Boot 的集成有很好的支持!**我们将在教程中使用 Flowable 和 Spring Boot 集成。
5. 使用流程引擎创建演示应用程序
现在让我们创建一个简单的应用程序,它封装了 Flowable 的流程引擎,并提供了一个基于 HTTP 的 API 来与 Flowable API 交互。也可能有一个 Web 或移动应用程序位于 API 之上,以使体验更好,但我们将在本教程中跳过它。
我们将创建我们的演示作为 Spring Boot 应用程序。
5.1. 依赖项
首先,让我们看看我们需要从 Maven 中提取的依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
我们需要的依赖项都可以在 Maven 中心获得:
- Spring Boot Starter for Web — 这是 Spring Boot 的标准启动器
- Flowable Starter for Spring Boot — 这是 Spring Boot Flowable Engines 所必需的
- H2 数据库 ——Flowable 需要一个数据库来存储数据,而 H2 是默认的内存数据库
5.2. 流程定义
**当我们启动 Spring Boot 应用程序时,它会尝试自动加载文件夹“resources/processes”下的所有流程定义。**因此,让我们使用上面创建的流程定义创建一个名为“article-workflow.bpmn20.xml”的 XML 文件,并将其放在该文件夹中。
5.3. 配置
正如我们所知道的,Spring Boot 对应用程序配置采取了一种高度自以为是的方法,对于作为 Spring Boot 的一部分的 Flowable 也是如此。例如,检测到 H2 作为类路径上唯一的数据库驱动程序,Flowable 会自动配置它以供使用。
显然,每个可配置的方面都可以通过应用程序属性 以自定义方式进行配置。但是,对于本教程,我们将坚持使用默认值!
5.4. Java 代表
在我们的流程定义中,我们使用了几个应该作为服务任务的一部分调用的 Java 类。这些类实现了JavaDelegate接口,在 Flowable 中称为 Java Delegates。我们现在将为这些 Java Delegates 定义虚拟类:
public class PublishArticleService implements JavaDelegate {
public void execute(DelegateExecution execution) {
System.out.println("Publishing the approved article.");
}
}
public class SendMailService implements JavaDelegate {
public void execute(DelegateExecution execution) {
System.out.println("Sending rejection mail to author.");
}
}
显然,我们必须将这些虚拟类替换为实际服务才能发布文章或发送电子邮件。
5.5. HTTP API
最后,让我们创建一些端点来与流程引擎交互并使用我们定义的流程。
我们将首先定义一个公开三个端点的控制器:
@RestController
public class ArticleWorkflowController {
@Autowired
private ArticleWorkflowService service;
@PostMapping("/submit")
public void submit(@RequestBody Article article) {
service.startProcess(article);
}
@GetMapping("/tasks")
public List<Article> getTasks(@RequestParam String assignee) {
return service.getTasks(assignee);
}
@PostMapping("/review")
public void review(@RequestBody Approval approval) {
service.submitReview(approval);
}
}
我们的控制器公开端点以提交一篇文章以供审查,获取要审查的文章列表,最后提交一篇文章的评论。Article和Approval是可以在存储库中找到的标准 POJO。
我们实际上将大部分工作委托给ArticleWorkflowService:
@Service
public class ArticleWorkflowService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Transactional
public void startProcess(Article article) {
Map<String, Object> variables = new HashMap<>();
variables.put("author", article.getAuthor());
variables.put("url", article.getUrl());
runtimeService.startProcessInstanceByKey("articleReview", variables);
}
@Transactional
public List<Article> getTasks(String assignee) {
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateGroup(assignee)
.list();
return tasks.stream()
.map(task -> {
Map<String, Object> variables = taskService.getVariables(task.getId());
return new Article(task.getId(), (String) variables.get("author"), (String) variables.get("url"));
})
.collect(Collectors.toList());
}
@Transactional
public void submitReview(Approval approval) {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("approved", approval.isStatus());
taskService.complete(approval.getId(), variables);
}
}
现在,这里的大部分代码都非常直观,但让我们了解一下要点:
- RuntimeService实例化特定提交的流程
- TaskService查询和更新任务
- 将所有数据库调用包装在 Spring 支持的事务中
- 将作者和 URL 等详细信息存储在Map中,并与流程实例一起保存;这些被称为流程变量,我们可以在流程定义中访问它们,正如我们之前看到的
现在,我们准备好测试我们的应用程序和流程引擎了。启动应用程序后,我们可以简单地使用curl或任何 REST 客户端(如 Postman)与我们创建的端点进行交互。
6. 单元测试过程
**Flowable 支持不同版本的 JUnit,包括 JUnit 5,用于为业务流程创建单元测试。**与 Spring 的 Flowable 集成也对此提供了适当的支持。让我们看一下 Spring 中流程的典型单元测试:
@ExtendWith(FlowableSpringExtension.class)
@ExtendWith(SpringExtension.class)
public class ArticleWorkflowUnitTest {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Test
@Deployment(resources = { "processes/article-workflow.bpmn20.xml" })
void articleApprovalTest() {
Map<String, Object> variables = new HashMap<>();
variables.put("author", "[[email protected]](/cdn_cgi/l/email_protection)");
variables.put("url", "http://blogdemo.com/dummy");
runtimeService.startProcessInstanceByKey("articleReview", variables);
Task task = taskService.createTaskQuery().singleResult();
assertEquals("Review the submitted tutorial", task.getName());
variables.put("approved", true);
taskService.complete(task.getId(), variables);
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
这应该看起来很像 Spring 中的标准单元测试,除了像*@Deployment这样的一些注释。现在,Flowable 提供了@Deployment*注解来创建和删除围绕测试方法的流程部署。
7. 了解流程的部署
虽然我们不会在本教程中介绍流程部署的细节,但值得介绍一些重要的方面。
通常,流程被归档为业务归档 (BAR) 并部署在应用程序中。在部署时,会扫描此存档以查找工件(如流程定义)并进行处理。您可能已经注意到以*“.bpmn20.xml”*结尾的流程定义文件的约定。
虽然我们在教程中使用了默认的内存 H2 数据库,但这实际上不能在实际应用程序中使用,原因很简单,内存数据库不会在启动时保留任何数据,并且实际上是不可能在集群环境中使用!因此,我们必须使用生产级关系数据库并在应用程序中提供所需的配置。
虽然 BPMN 2.0 本身没有任何版本控制的概念,但Flowable 为流程创建了一个版本属性,该属性部署在数据库中。如果部署了由属性“id”标识的同一进程的更新版本,则会创建一个新条目,其中版本递增。当我们尝试通过“id”启动流程时,流程引擎会获取已部署的流程定义的最新版本。
如果我们使用前面讨论过的设计器之一来创建流程定义,我们已经有了流程的可视化。**我们可以将流程图导出为图像,并将其放在 XML 流程定义文件旁边。**如果我们坚持 Flowable 建议的标准命名约定 ,该图像将与流程本身一起由流程引擎处理。此外,我们还可以通过 API 获取此图像!
8. 进程实例的浏览历史
在业务流程的情况下,了解过去发生的事情通常至关重要。我们可能需要它来进行简单的调试或复杂的法律审计。
**Flowable 记录流程执行过程中发生的事情,并将其保存在数据库中。**此外,Flowable 可以通过 API 查询和分析这些历史记录。Flowable 记录了六个实体,HistoryService有方法来查询它们。
让我们看一个简单的查询来获取完成的流程实例:
HistoryService historyService = processEngine.getHistoryService();
List<HistoricActivityInstance> activities = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(processInstance.getId())
.finished()
.orderByHistoricActivityInstanceEndTime()
.asc()
.list();
正如我们所见,**查询记录数据的 API 是相当可组合的。**在此示例中,我们通过 ID 查询已完成的流程实例,并按结束时间的升序对它们进行排序。
9. 监控过程
监控是任何关键业务应用程序的一个关键方面,对于处理组织业务流程的应用程序更是如此。Flowable 有几个选项可以让我们实时监控流程。
**Flowable 提供了我们可以通过 JMX 访问的特定 MBean,**不仅可以收集数据进行监控,还可以执行许多其他活动。我们可以将它与任何标准 JMX 客户端集成,包括jconsole,它与标准 Java 发行版一起存在。
使用 JMX 进行监控会带来很多选择,但相对复杂且耗时。但是,由于我们使用的是 Spring Boot,所以我们很幸运!
Spring Boot 提供 Actuator Endpoints 以通过 HTTP 收集应用程序指标。我们可以将它与Prometheus 和 Grafana 等工具堆栈无缝集成,以最小的努力创建生产级监控工具。
**Flowable 提供了一个额外的 Actuator Endpoint 暴露有关正在运行的进程的信息。**这不如通过 JMX 收集信息好,但它快速、简单,最重要的是,足够了。