初識Activiti工作流
一、背景介紹
公司最近接了一個監獄AB門系統的專案,在對專案進行調研時,發現客戶的關注點主要是在AB門流程這塊,專案大部分功能都是審批流程和單據流動狀態等。而之前公司的專案關於流程主要都是在表中設定狀態後使用程式碼進行流程控制,正好基於此專案,將工作流框架整合到專案中,記錄整合過程中框架知識和在整合過程中的問題。
二、Activiti與JBPM對比
在進行工作流框架選擇上,根據市場佔用率主要有Activiti和jBPM。既然這樣,我們就在這兩個框架中選擇一個,由於首次引入專案,主要考慮的還是當遇到問題時社群比較活躍和後期的一個擴充套件。
技術組成 | Activiti | jBPM |
ORM框架 | Mybatis3 | Hibernate3 |
持久化標準 | 無 | EJB JPA規範 |
事務管理 | Mybatis自帶 / Spring整合事務 | Bitronix / 基於JTA事務管理 |
資料庫連線方式 | Jdbc / DataSource | Jdbc / DataSource |
Spring支援 | 天然支援Spirng | 預設未提供對Spring支援 |
支援的資料庫 | Oracle / SQLServer / MySQL / H2 / 記憶體資料庫 | Oracle / SQLServer / MySQL / 記憶體資料庫 |
設計模式 | 命令模式 / 觀察者模式 等 | 無 |
內部服務通訊 | Service間通過API呼叫 | 基於Apache Mina非同步通訊 |
整合介面 | SOAP / Mule / RESTful | 訊息通訊 |
支援的流程格式 | BPMN2 / xPDL / jPDL 等 | 只支援BPMN2 xml |
引擎核心 | PVM流程虛擬機器 | Drools |
技術前身 | jBPM3 / jBPM4 | Drools Flow |
外掛工具 | IDEA的actiBPM / Eclipse的Eclipse Designer / 提供基於REST風格的Activit Explorer | 提供Eclipse外掛和Web應用管理 |
更新週期 | 大約每兩個月釋出一次 | 不定時 |
基於以上及專案需求,我們選擇Activiti作為專案的工作流框架,其中Activiti中5,6,7各個版本又有些不同,根據穩定性和社群情況,我們最終選擇Activiti6.X。
三、工作流基礎
3.1 什麼時BPM
BPM即Business Process Management的縮寫,為業務流程管理。是一套達成企業各種業務環節整合的全面管理模式。BPM是一系列邏輯相關的活動的集合,BPM最早是由工作流和企業應用整合融合發展而來,當時是為了滿足無紙化辦公需求。
3.2 工作流的生命週期
一個完整的工作流生命週期主要有5步:
1、定義:即流程的定義,所有的流程總是從定義開始。主要任務是收集需求並將其轉化為流程定義。
2、釋出:開發人員將資源打包後在系統平臺中釋出流程定義,主要任務流程定義檔案/自定義表單/任務監聽類等。
3、執行:具體的流程引擎按照上面定義的流程處理路線來執行業務。
4、監控:收集每個任務的結果,將根據不同結果來做處理。
5、優化:此時業務流程已經完成,需要的就是優化流程或重新設計等。
3.3 什麼時BPMN
即Business Process Modeling Notation的簡稱,全稱為業務流程建模標註 ,由BPMN標準組織釋出,2011年釋出到2.0之後,市場常用的都是此版本規範。
BPMN定義類業務流程圖,其基於流程圖技術,同時對建立業務流程操作的圖形化模型進行了裁剪。業務流程的模型即圖形化物件的網圖,包含有活動和定義操作順序的流程控制。
四、Activiti介紹
4.1 什麼是Activiti
Activiti是一個針對企業使用者、開發人員、系統管理員的輕量級工作流業務管理平臺,其核心是使用Java開發的快速、穩定的BPMN2.0流程引擎。Activiti是在ApacheV2許可下發布的,可以執行在任何型別的Java程式中,如:伺服器、叢集、雲服務等。Activiti可以完美的與Spring整合,是基於簡約的設計思想而建立。
4.2 Activiti的特點
1、資料持久化:Activiti設計思想是簡潔與快速。一般情況下系統的瓶頸主要就體現在應用和資料庫的互動上,針對這種情況Activiti選擇了使用Mybatis,而通過最優SQL語句執行Command。
2、引擎Service介面:Activiti引擎提供了七大Service介面,都是通過ProcessEngine獲取,同時支援鏈式API程式設計風格。
Service介面 | 作用 |
RepositoryService | 流程倉庫Service,用於管理流程倉庫,如:部署、刪除、讀取流程資源 |
IdentifyService | 身份Service,可管理和查詢使用者、組之間的關係 |
RuntimeService | 執行時Service,處理所有正在執行的任務和流程例項等 |
TaskService | 任務Service,用於管理查詢任務,如簽收、辦理、指派等 |
FormService | 表單Service,用於讀取和任務、流程相關的表單資料 |
HistoryService | 歷史Service,可查詢所有歷史資料 |
ManagementService | 引擎管理Service,和具體業務無關,可用查詢引擎配置、資料庫、作業等 |
3、流程設計器:Activiti團隊設計了基於BPMN2.0規範的設計器-Eclipse Designer,除此還有Signavio公司為Activiti定製的基於Web的Activiti Modeler流程設計器。
4、原生支援Spring:當前企業開發,基本上都會基於Spring去開發自己的系統,由於Activiti原生支援Spring,所以很輕鬆地進行Spring整合。
5、分離執行時與歷史資料:執行與歷史資料的分離,可以加快執行時資料的效能,當需要歷史資料時,我們在去查詢。
4.3 Activiti的應用
1、在系統整合方面:與ESB整合 / 與規則引擎整合 / 嵌入已有系統平臺(也是本專案的需求)
2、在其他產品中應用:Alfresco公司的ECM產品在企業中應用,主要涉及文件管理 / 協作 / 記錄管理 / 知識庫管理 / Web內容管理等。
4.4 Activiti框架與元件
Activiti最重要的就是引擎,除此之外就是外部的工具和元件。
Modeling | Runtion | Management |
Activiti Modeler | Activiti Engine | Activiti Exproler |
Activiti Designer | Activiti REST | |
Activiti Kickstart |
下面對以上元件進行簡單的說明:
1、Activiti Engine:最核心的模組,提供針對BPMN2.0規範的解析 / 執行 / 建立 / 管理(任務-流程例項) / 查詢歷史記錄並生成相應報表等。
2、Activiti Modeler:模型設計器,非Activiti公司開發。用於將需求轉換為規範流程定義。
3、Activiti Designer:設計器,與Activiti Modeler功能類似。
4、Activiti Exproler:用來管理倉庫 / 使用者 / 組,啟動流程 / 任務辦理等。
5、Activiti REST:提供REST風格的服務,允許客戶端以JSON的方式與引擎的REST API互動,協議具有跨平臺 / 跨語言。
五、搭建Activiti開發環境並測試
5.1 下載Activiti
從Activiti下載6.0版本並將其解壓,我們可對目錄結構進行說明。
1、database:該資料夾包含Activiti引擎表的建立 / 刪除 / 版本升級三種類型的SQL指令碼,命名規則為 “activiti.[資料庫型別].[指令碼型別create|drop|upgradestep].[其他].sql”,升級指令碼包含 xx.to.yy。
2、docs:該目錄包含javadocs(開發文件) / userguide(使用者手冊) / xsd(與流程定義相關的scheme)
3、libs:包含Activiti引擎的各個模組包。
4、wars:一些可以獨立執行的專案,用來熟悉Activiti操作。activiti-explorer.war / activiti-rest.war。
5.2 配置環境
1、安裝JDK並配置環境變數。
2、安裝Ant
3、安裝Maven
以上三個環境的安裝方法到對應官方網站下載安裝包,並按照官方文件說明來安裝。
5.3 配置檔案介紹
1、Activiti配置檔案,我們首先開看一個標準的Activiti配置檔案內容。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> 7 <property name="databaseType" value="h2" /> 8 <property name="databaseSchemaUpdate" value="true" /> 9 <property name="jobExecutorActivate" value="false" /> 10 <property name="history" value="full" /> 11 </bean> 12 13 </beans>
對Spring熟悉的同學一看該配置檔案就知道這是一個普通的Spring配置檔案,從這也可體現出Activiti對Spring的原生支援。
關於相關的配置見下圖:
2、Maven配置,在Maven專案的pom檔案中引入依賴
<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>6.0.0</version> </dependency>
5.4 一個簡單的例項
下面我們就使用一個請假流程來對Activit做簡單的實戰。由請假人發起,部門領導審批,完成。
首先我們需要建立流程圖:
這就是流程圖設計器的介面,流程設計總是從start開始且只有一個入口,總是由End結束可有多個結束。中間就是任務、結果、流程線等,關於各個節點後續再做介紹,基於此流程圖我們來看看程式碼時如何實現的。
1 /** 2 * 該例項測試簡單流程包含審批和審批結果 3 * @throws Exception 4 */ 5 @Test 6 public void testStartProcess2() throws Exception { 7 // 1 建立流程引擎,使用記憶體資料庫 8 ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration().buildProcessEngine(); 9 10 // 2 部署流程定義檔案 11 RepositoryService repositoryService = processEngine.getRepositoryService(); 12 // String bpmnFileName = "leave2.bpmn"; 13 String bpmnFileName = "leave2.bpmn.xml"; 14 repositoryService.createDeployment().addInputStream("leave2.bpmn", this.getClass().getClassLoader().getResourceAsStream(bpmnFileName)).deploy(); 15 16 // 3 驗證已部署的流程定義 17 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult(); 18 assertEquals("leave2", processDefinition.getKey()); 19 20 // 4 啟動流程並返回流程例項 21 RuntimeService runtimeService = processEngine.getRuntimeService(); 22 23 Map<String, Object> variables = new HashMap<>(); 24 variables.put("applyUser", "employee1"); //申請人名稱 25 variables.put("days", 3); //請假天數 26 27 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave2", variables); 28 assertNotNull(processInstance); 29 System.out.println("pid=" + processInstance.getId() + ", pdid=" + processInstance.getProcessDefinitionId()); 30 31 TaskService taskService = processEngine.getTaskService(); 32 Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult(); //查詢deptLeader組未簽收的任務 33 assertNotNull(deptLeaderTask); 34 assertEquals("領導審批", deptLeaderTask.getName()); 35 taskService.claim(deptLeaderTask.getId(), "leaderUser"); //簽收此任務歸使用者leaderUser所有 36 37 variables = new HashMap<>();//.clear(); //處理結果 38 variables.put("approved", true); //設定審批通過 39 40 taskService.complete(deptLeaderTask.getId(), variables); //設定審批結果 41 42 deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult(); //由於任務已經完成,此時查詢任務應該為空 43 assertNull(deptLeaderTask); 44 45 // 獲取歷史記錄 46 HistoryService historyService = processEngine.getHistoryService(); 47 long count = historyService.createHistoricProcessInstanceQuery().finished().count(); //已經完成的流程例項數 48 assertEquals(1, count); 49 }
執行結果:
1 pid=4, pdid=leave2:1:3 2 applyUser:employee1, days:3, approval:true