一、背景介紹
公司最近接了一個監獄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