Flowable學習入門


一、Flowable簡介

 

1、Flowable是什么

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。 下面的教程從設置與使用核心引擎的介紹開始。后續章節都建立在之前章節中獲取的知識之上。

 

2、Flowable與Activiti

Flowable,2016年基於Activiti誕生。

在這里插入圖片描述

 

二、開始

1、構建命令行程序

我們將構建的例子是一個簡單的請假(holiday request)流程:

  • 雇員(employee)申請幾天的假期
  • 經理(manager)批准或駁回申請
  • 我們會模擬將申請注冊到某個外部系統,並給雇員發送結果郵件

 

1.1、創建流程引擎

1.1.1、創建maven項目

創建一個名為holiday-request的maven項目,添加依賴:

        <!--Flowable流程引擎-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.3.0</version> </dependency> <!--MySQL驅動,這里采用MySQL數據庫,如果采用其它數據庫,需要引入對應的依賴。--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> 

 

1.1.2、創建數據庫表格

  • 創建一個數據庫flowable_demo。

  • 創建一個普通的Java類:HolidayRequest

/** * @Author 三分惡 * @Date 2020/5/2 * @Description 創建表格 */ public class HolidayRequest { public static void main(String[] args) { //1、創建ProcessEngineConfiguration實例,該實例可以配置與調整流程引擎的設置 ProcessEngineConfiguration cfg=new StandaloneProcessEngineConfiguration() //2、通常采用xml配置文件創建ProcessEngineConfiguration,這里直接采用代碼的方式 //3、配置數據庫相關參數 .setJdbcUrl("jdbc:mysql://localhost:3306/flowable_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true") .setJdbcUsername("root") .setJdbcPassword("root") .setJdbcDriver("com.mysql.jdbc.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); //4、初始化ProcessEngine流程引擎實例 ProcessEngine processEngine=cfg.buildProcessEngine(); } } 

 

1.1.3、運行

運行該類,會發現在數據庫flowable_demo中創建了34個表:

在這里插入圖片描述

1.1.4、創建的表格說明

flowable命名規則:

  • ACT_RE_* :’ RE ’表示repository(存儲)。RepositoryService接口操作的表。帶此前綴的表包含的是靜態信息,如,流程定義,流程的資源(圖片,規則等)。
  • ACT_RU_* :’ RU ’表示runtime。這是運行時的表存儲着流程變量,用戶任務,變量,職責(job)等運行時的數據。flowable只存儲實例執行期間的運行時數據,當流程實例結束時,將刪除這些記錄。這就保證了這些運行時的表小且快。
  • ACT_ID_* : ’ ID ’表示identity(組織機構)。這些表包含標識的信息,如用戶,用戶組,等等。
  • ACT_HI_* : ’ HI ’表示history。就是這些表包含着歷史的相關數據,如結束的流程實例,變量,任務,等等。
  • ACT_GE_* : 普通數據,各種情況都使用的數據。

34張表說明:

表分類 表名 表說明
一般數據(2) ACT_GE_BYTEARRAY 通用的流程定義和流程資源
ACT_GE_PROPERTY 系統相關屬性
流程歷史記錄(8) ACT_HI_ACTINST 歷史的流程實例
ACT_HI_ATTACHMENT 歷史的流程附件
ACT_HI_COMMENT 歷史的說明性信息
ACT_HI_DETAIL 歷史的流程運行中的細節信息
ACT_HI_IDENTITYLINK 歷史的流程運行過程中用戶關系
ACT_HI_PROCINST 歷史的流程實例
ACT_HI_TASKINST 歷史的任務實例
ACT_HI_VARINST 歷史的流程運行中的變量信息
用戶用戶組表(9) ACT_ID_BYTEARRAY 二進制數據表
ACT_ID_GROUP 用戶組信息表
ACT_ID_INFO 用戶信息詳情表
ACT_ID_MEMBERSHIP 人與組關系表
ACT_ID_PRIV 權限表
ACT_ID_PRIV_MAPPING 用戶或組權限關系表
ACT_ID_PROPERTY 屬性表
ACT_ID_TOKEN 系統登錄日志表
ACT_ID_USER 用戶表
流程定義表(3) ACT_RE_DEPLOYMENT 部署單元信息
ACT_RE_MODEL 模型信息
ACT_RE_PROCDEF 已部署的流程定義
運行實例表(10) ACT_RU_DEADLETTER_JOB 正在運行的任務表
ACT_RU_EVENT_SUBSCR 運行時事件
ACT_RU_EXECUTION 運行時流程執行實例
ACT_RU_HISTORY_JOB 歷史作業表
ACT_RU_IDENTITYLINK 運行時用戶關系信息
ACT_RU_JOB 運行時作業表
ACT_RU_SUSPENDED_JOB 暫停作業表
ACT_RU_TASK 運行時任務表
ACT_RU_TIMER_JOB 定時作業表
ACT_RU_VARIABLE 運行時變量表
其他表(2) ACT_EVT_LOG 事件日志表
ACT_PROCDEF_INFO 流程定義信息

 

1.1.4、日志配置

在上面的運行中,同時可以看到,控制台有報錯的信息,這是日志沒有正確地配置:

在這里插入圖片描述

Flowable使用SLF4J作為內部日志框架。我們使用log4j作為SLF4J的實現。因此在pom.xml文件中添加下列依賴:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> 

 

在 src/resouce目錄下新建log4j的配置文件log4j.properties:

log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n 

 

再次運行,可以看到關於引擎啟動與創建數據庫表結構的提示日志:

在這里插入圖片描述

1.2、部署流程定義

要構建的流程是一個非常簡單的請假流程。Flowable引擎需要流程定義為BPMN 2.0格式,這是一個業界廣泛接受的XML標准。

在Flowable術語中,我們將其稱為一個流程定義(process definition)。一個流程定義可以啟動多個流程實例(process instance)。流程定義可以看做是重復執行流程的藍圖。 在這個例子中,流程定義定義了請假的各個步驟,而一個流程實例對應某個雇員提出的一個請假申請。

BPMN 2.0存儲為XML,並包含可視化的部分:使用標准方式定義了每個步驟類型(人工任務,自動服務調用,等等)如何呈現,以及如何互相連接。這樣BPMN 2.0標准使技術人員與業務人員能用雙方都能理解的方式交流業務流程。

我們要使用的流程定義為:

在這里插入圖片描述

流程定義的說明:

  • 我們假定啟動流程需要提供一些信息,例如雇員名字、請假時長以及說明。當然,這些可以單獨建模為流程中的第一步。 但是如果將它們作為流程的“輸入信息”,就能保證只有在實際請求時才會建立一個流程實例。否則(將提交作為流程的第一步),用戶可能在提交之前改變主意並取消,但流程實例已經創建了。 在某些場景中,就可能影響重要的指標(例如啟動了多少申請,但還未完成),取決於業務目標。

  • 左側的圓圈叫做啟動事件(start event)。這是一個流程實例的起點。

  • 第一個矩形是一個用戶任務(user task)。這是流程中人類用戶操作的步驟。在這個例子中,經理需要批准或駁回申請。

  • 取決於經理的決定,排他網關(exclusive gateway) (帶叉的菱形)會將流程實例路由至批准或駁回路徑。

  • 如果批准,則需要將申請注冊至某個外部系統,並跟着另一個用戶任務,將經理的決定通知給申請人。當然也可以改為發送郵件。

  • 如果駁回,則為雇員發送一封郵件通知他。

一般來說,這樣的流程定義使用可視化建模工具建立,如Flowable Designer(Eclipse)或Flowable Web Modeler(Web應用)。

這里我們直接撰寫XML,以熟悉BPMN 2.0及其概念。

以下是與上面展示的流程圖對應的BPMN 2.0 XML。這里只包含了“流程部分”。如果使用圖形化建模工具,實際的XML文件還將包含“可視化部分”,用於描述圖形信息,如流程定義中各個元素的坐標(所有的圖形化信息包含在XML的BPMNDiagram標簽中,作為definitions標簽的子元素)。

在src/main/resources文件夾下創建為holiday-request.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: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="holiday-request" name="Holiday Request" isExecutable="true"> <!--開始事件:流程實例的起點--> <startEvent id="startEvent"/> <!--順序流:執行時會從一個活動流向另一個活動--> <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/> <!--用戶任務:需要人工來進行操作--> <userTask id="approveTask" name="Approve or reject request"/> <sequenceFlow sourceRef="approveTask" targetRef="decision"/> <!--排他網關--> <exclusiveGateway id="decision"/> <sequenceFlow sourceRef="decision" targetRef="externalSystemCall"> <!--順序流條件:以表達式(expression)的形式定義了條件(condition) --> <conditionExpression xsi:type="tFormalExpression"> <!--條件表達式:是${approved == true}的簡寫--> <![CDATA[ ${approved} ]]> </conditionExpression> </sequenceFlow> <sequenceFlow sourceRef="decision" targetRef="sendRejectionMail"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[ ${!approved} ]]> </conditionExpression> </sequenceFlow> <!--服務任務,一個自動活動,它會調用一些服務--> <serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="edu.hpu.process.CallExternalSystemDelegate"/> <userTask id="holidayApprovedTask" name="Holiday Approve!"/> <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/> <serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="edu.hpu.process.SendRejectionMail"/> <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/> <!--結束事件--> <endEvent id="approveEnd"/> <endEvent id="rejectEnd"/> </process> </definitions> 

 

  • 每一個步驟(在BPMN 2.0術語中稱作活動(activity))都有一個id屬性,為其提供一個在XML文件中唯一的標識符。所有的活動都可以設置一個名字,以提高流程圖的可讀性。

  • 活動之間通過順序流(sequence flow)連接,在流程圖中是一個有向箭頭。在執行流程實例時,執行(execution)會從啟動事件沿着順序流流向下一個活動。

  • 離開排他網關(帶有X的菱形)的順序流很特別:都以表達式(expression)的形式定義了條件(condition) 。當流程實例的執行到達這個網關時,會計算條件,並使用第一個計算為true的順序流。這就是排他的含義:只選擇一個。當然如果需要不同的路由策略,可以使用其他類型的網關。

  • 這里用作條件的表達式為approvedapproved,這是{approved == true}的簡寫。變量’approved’被稱作流程變量(process variable)。流程變量是持久化的數據,與流程實例存儲在一起,並可以在流程實例的生命周期中使用。在這個例子里,我們需要在特定的地方(當經理用戶任務提交時,或者以Flowable的術語來說,完成(complete)時)設置這個流程變量,因為這不是流程實例啟動時就能獲取的數據。

現在我們已經有了流程BPMN 2.0 XML文件,下來需要將它部署(deploy)到引擎中。部署一個流程定義意味着:

  • 流程引擎會將XML文件存儲在數據庫中,這樣可以在需要的時候獲取它。

  • 流程定義轉換為內部的、可執行的對象模型,這樣使用它就可以啟動流程實例。

將流程定義部署至Flowable引擎,需要使用RepositoryService,其可以從ProcessEngine對象獲取。使用RepositoryService,可以通過XML文件的路徑創建一個新的部署(Deployment),並調用deploy()方法實際執行:

       //創建RepositoryService實例 RepositoryService repositoryService=processEngine.getRepositoryService(); //加載流程 Deployment deployment=repositoryService.createDeployment() .addClasspathResource("holiday-request.bpmn20.xml") .deploy(); 

 

我們現在可以通過API查詢驗證已經部署在引擎中的流程定義。通過RepositoryService創建的ProcessDefinitionQuery對象實現。

        //查詢流程定義 ProcessDefinition processDefinition=repositoryService.createProcessDefinitionQuery() .deploymentId(deployment.getId()) .singleResult(); System.out.println("Found process definition : "+processDefinition.getName()); 

運行:

在這里插入圖片描述

xml文件已經存儲進了數據庫:

在這里插入圖片描述

 

1.3、啟動流程實例

現在已經在流程引擎中部署了流程定義,因此可以使用這個流程定義作為“藍圖”啟動流程實例。

  • 要啟動流程實例,需要提供一些初始化流程變量。一般來說,可以通過呈現給用戶的表單,或者在流程由其他系統自動觸發時通過REST API,來獲取這些變量。在這個例子里,我們簡化為使用java.util.Scanner類在命令行輸入一些數據:
       //1、獲取流程初始化變量 Scanner scanner = new Scanner(System.in); System.out.println("Who are you?"); String employee = scanner.nextLine(); System.out.println("How many holidays do you want to request?"); Integer nrOfHolidays = Integer.valueOf(scanner.nextLine()); System.out.println("Why do you need them?"); String description = scanner.nextLine(); 
  • 接下來,我們使用RuntimeService啟動一個流程實例。收集的數據作為一個java.util.Map實例傳遞,其中的鍵就是之后用於獲取變量的標識符。這個流程實例使用key啟動(還有其它方式)。這個key就是BPMN 2.0 XML文件中設置的id屬性,在這個例子里是holiday-request。
<process id="holiday-request" name="Holiday Request" isExecutable="true"> 
     //2、使用RuntimeService啟動一個流程實例 RuntimeService runtimeService=processEngine.getRuntimeService(); Map<String, Object> variables = new HashMap<String, Object>(); variables.put("employee", employee); variables.put("nrOfHolidays", nrOfHolidays); variables.put("description", description); ProcessInstance processInstance=runtimeService.startProcessInstanceByKey("holiday-request",variables); 

在流程實例啟動后,會創建一個執行(execution),並將其放在啟動事件上。從這里開始,這個執行會沿着順序流移動到經理審批的用戶任務,並執行用戶任務行為。這個行為將在數據庫中創建一個任務,該任務可以之后使用查詢找到。用戶任務是一個等待狀態(wait state),引擎會停止執行,返回API調用處。

輸入流程初始化變量:
在這里插入圖片描述

將數據插入數據庫中

在這里插入圖片描述

向數據庫中插入了數據:
在這里插入圖片描述

 

1.4、Flowable中的事務

在Flowable中,數據庫事務扮演了關鍵角色,用於保證數據一致性,並解決並發問題。當調用Flowable API時,默認情況下,所有操作都是同步的,並處於同一個事務下。這意味着,當方法調用返回時,會啟動並提交一個事務。

流程啟動后,會有一個數據庫事務從流程實例啟動時持續到下一個等待狀態。在這個例子里,指的是第一個用戶任務。當引擎到達這個用戶任務時,狀態會持久化至數據庫,提交事務,並返回API調用處。

在Flowable中,當一個流程實例運行時,總會有一個數據庫事務從前一個等待狀態持續到下一個等待狀態。數據持久化之后,可能在數據庫中保存很長時間,甚至幾年,直到某個API調用使流程實例繼續執行。請注意當流程處在等待狀態時,不會消耗任何計算或內存資源,直到下一次APi調用。

在這個例子中,當第一個用戶任務完成時,會啟動一個數據庫事務,從用戶任務開始,經過排他網關(自動邏輯),直到第二個用戶任務。或通過另一條路徑直接到達結束。

 

1.5、查詢與完成任務

在更實際的應用中,會為雇員及經理提供用戶界面,讓他們可以登錄並查看任務列表。其中可以看到作為流程變量存儲的流程實例數據,並決定如何操作任務。在這個例子中,我們通過執行API調用來模擬任務列表,通常這些API都是由UI驅動的服務在后台調用的。

我們還沒有為用戶任務配置辦理人。

  • 將第一個任務指派給"經理(managers)"組,而第二個用戶任務指派給請假申請的提交人。
        <!--將任務指派給經理組-->
        <userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/> 
       <!--指派給請假審批的審批人,${employee}使用流程變量動態指派,在流程實例啟動時傳遞-->
        <userTask id="holidayApprovedTask" name="Holiday Approve!" flowable:assignee="${employee}"/> 

 

  • 要獲得實際的任務列表,需要通過TaskService創建一個TaskQuery。這個查詢配置為只返回’managers’組的任務:
        //通過TaskService查詢 manager 組的任務 TaskService taskService=processEngine.getTaskService(); List<Task> tasks=taskService.createTaskQuery().taskCandidateGroup("managers").list(); System.out.println("You have " + tasks.size() + " tasks:"); for (int i=0; i<tasks.size(); i++) { System.out.println((i+1) + ") " + tasks.get(i).getName()); } 

 

  • 使用集合下標獲取特定流程實例的變量,在控制台輸出
        //使用集合下標獲取特定流程實例的變量,在控制台輸出 System.out.println("Which task would you like to complete?"); int taskIndex = Integer.valueOf(scanner.nextLine()); Task task=tasks.get(taskIndex-1); Map<String,Object> processVariables=taskService.getVariables(task.getId()); System.out.println(processVariables.get("employee") + " wants " + processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?"); 

 

  • 經理現在就可以完成任務了。在實際開發中,通常由用戶提交一個表單。表單中的數據作為流程變量傳遞。在這里,我們在完成任務時傳遞帶有’approved’變量(這個名字很重要,因為之后會在順序流的條件中使用)的map來模擬:
        //經理完成任務 boolean approved=scanner.nextLine().toLowerCase().equals("y"); variables = new HashMap<String, Object>(); variables.put("approved", approved); //任務完成,並會在離開排他網關的兩條路徑中,基於’approved ’流程變量選擇一條 taskService.complete(task.getId(),variables); 

現在還缺最后一點,服務任務調用的服務沒有實現:

       <!--服務任務,一個自動活動,它會調用一些服務-->
        <serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="edu.hpu.process.CallExternalSystemDelegate"/> 
  • 創建一個類,實現JavaDelegate接口,實現execute方法,這個方法可以寫很多業務邏輯,這里我們只是是在控制台打印輸出一些內容:
/** * @Author 三分惡 * @Date 2020/5/3 * @Description */ public class CallExternalSystemDelegate implements JavaDelegate { public void execute(DelegateExecution delegateExecution) { System.out.println("Calling the external system for employee " + delegateExecution.getVariable("employee")); } } 

運行:

啟動流程:

在這里插入圖片描述

  • 查看任務
    在這里插入圖片描述

  • 完成任務
    在這里插入圖片描述

  • 執行自動邏輯
    在這里插入圖片描述

至此,一個模擬請假流程就完成了。

 

1.6、使用歷史數據

選擇使用Flowable這樣的流程引擎的原因之一,是它可以自動存儲所有流程實例的審計數據或歷史數據。這些數據可以用於創建報告,深入展現組織運行的情況,瓶頸在哪里,等等。

從ProcessEngine獲取HistoryService,並創建歷史活動(historical activities)的查詢。

 //獲取HistoryService實例 HistoryService historyService=processEngine.getHistoryService(); //添加查詢條件 List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery() //選擇特定實例 .processInstanceId(processInstance.getId()) //選擇已完成的 .finished() //根據實例完成時間升序排列 .orderByHistoricActivityInstanceEndTime().asc() .list(); for (HistoricActivityInstance activity : activities) { System.out.println(activity.getActivityId() + " took " + activity.getDurationInMillis() + " milliseconds"); } 



參考:

【1】:Flowable BPMN 用戶手冊 (v 6.3.0)
【2】:flowable學習(Ⅰ) --- 創建一個簡單的flowable流程例子
【3】:activiti與flowable的區別
【4】:Flowable數據庫表說明
【5】:基於BPMN2.0的工作流(Workflow)
【6】:BPMN 2.0 / Flowable
【7】:BPMN 2.0規范
【8】:第 3 章 BPMN 2.0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM