目錄
Activiti 概述 與 下載
1、工作流(Workflow):是對工作流程及其各個操作步驟之間業務規則的抽象、概況和描述。
2、工作流模型:將工作流程中的工作如何前後組織在一起的邏輯和規則,在計算機中以恰當的模型表達並對其實施計算。
3、工作流要解決的問題是實現某個業務目標、利用計算機在多個參與者之間按某種預定規則自動傳遞文檔、信息或者任務。
4、主流的工作流框架:Activiti、OSWorkflow、JBoss 的 JBPM、Apache 的 ofBiz 等。生活中有很多的流程管理,比如請假流程、報銷流程、審批流程等等。
5、Activiti 歷史:JBPM 4 的時候團隊內部發生分歧,其中部分開發人員脫離了大部隊(JBoss),出來獨立寫出了 Activiti5;JBoss 則繼續 JBPM 的開發,後續又有了 JBPM 5 ...。
6、所以 Activiti 5 基本等同於 JBPM4,2016 年 11 月開源了 Activiti 5.22.0,2017 年 5 月開源了 Activiti 6.0.0,2018 年開始開源 Activiti 7.X。Activiti 7 分爲了 Activiti Core 與 Activiti Cloud 兩個方向,更傾向於雲環境、微服務。
7、Activiti 易於與 Spring、Spring boot、Spring cloud 集成,BPM 全稱 Business Process Management - 業務流程管理
8、Activiti 支持主流的數據庫(官方文檔):
Activiti database type | Example JDBC URL | Notes |
---|---|---|
h2 |
jdbc:h2:tcp://localhost/activiti |
Default configured database |
mysql |
jdbc:mysql://localhost:3306/activiti?autoReconnect=true |
Tested using mysql-connector-java database driver |
oracle |
jdbc:oracle:thin:@localhost:1521:xe |
|
postgres |
jdbc:postgresql://localhost:5432/activiti |
|
db2 |
jdbc:db2://localhost:50000/activiti |
|
mssql |
jdbc:sqlserver://localhost:1433;databaseName=activiti (jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver) OR jdbc:jtds:sqlserver://localhost:1433/activiti (jdbc.driver=net.sourceforge.jtds.jdbc.Driver) |
Tested using Microsoft JDBC Driver 4.0 (sqljdbc4.jar) and JTDS Driver |
Activiti 官網:https://www.activiti.org
Activiti Github 開源地址:https://github.com/Activiti/Activiti
Activiti v7.X 官方開發手冊:https://activiti.gitbook.io/activiti-7-developers-guide/
Activiti v6.X 官方開發手冊:https://www.activiti.org/userguide/
Activiti v5.X 官方開始手冊:https://www.activiti.org/5.x/userguide/
9、maven 依賴:非 Spring Boot 項目,使用 activiti-engine 即可,否則推薦使用 activiti-spring-boot-starter-basic,activiti 與 spring boot 集成的啓動器,裏面包含了 activiti-engine 引擎,以及 activiti 與 spring 的集成,以及 jdbc 等。
<!-- https://mvnrepository.com/artifact/org.activiti/activiti-engine -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<!--activiti 官網地址:https://www.activiti.org/userguide/#_getting_started_2-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
Activiti Hello World
1、千里之行,始於足下。學習一個新框架得先有一個 Hello World 壓壓驚,否則會很迷茫。
2、本文環境:Java JDK 1.8 + IDEA 2018 +Maven 3.1.6 + Activiti 6.0.0 + H2 Database + Spring Boot 2.1.4 進行開發。
1)使用 Maven 管理,不再去 Activiti 官網下載 jar 包,然後導入到項目中
2)暫時使用 H2 內存數據庫,讓 Activiti 的表和數據先直接在內存數據庫中使用。
3、特別提醒:
1)Acticiti 工作流開發,第一步是需要繪製工作流程圖(本文開頭的那種圖),第二步就是編碼。
2)下載 Acticiti 6.0.0 開發包或者源碼可以看到,裏面提供了針對各種數據庫的 sql 文件,然後實際中並不需要手動去執行這些 sql 腳本,因爲在後臺代碼中構建流程引擎(ProcessEngine)的時候,它可以自動執行 sql 建表腳本創建好 28 張表
3)Activiti 6.0.0 有 28 張表,使用 Activiti API 操作工作流的時候,Activiti 會自動操作自己的數據庫表。所以對於 activiti 的 28 張數據庫表,前期並不需要關心,對我們來說是透明的,入門之後可以再去深入瞭解。
安裝流程設計器插件
官網介紹地址:https://www.activiti.org/userguide/#eclipseDesignerInstallation
1、eclipse、sts、idea 都有繪製 activiti 流程圖的插件,但官方文檔介紹的是 eclipse(sts本質也是eclipse),所以這裏也採用 在 sts 上安裝流程圖插件。(注:在 sts 插件上繪製好流程圖後,本人更喜歡 IDEA 編碼,所以後續只需要將 sts 中創建好的 .bpmn 文件拷貝過去即可)
1、這裏選擇在線安裝插件 activiti 插件,打開 help -> Install New Software 安裝新的軟件(插件),在 Install 面板中,點擊 Add 按鈕添加插件名稱與地址:
Name: Activiti BPMN designer (名稱可以隨意取)
Location: http://activiti.org/designer/update/
2、點擊 "Next" 下一步,然後會聯網進行下載,之後在 "Install Detail" 面板直接 "Next",然後進入 "Review license" 查看許可證面板,點擊第一項 "I accept ..." 同意,最後點擊 "Finish" 完成安裝。
3、安裝過程中可能會彈框提示,選擇安裝所有即可,然後會提示重啓,重啓之後可以點擊菜單 "Window -> Show View -> Other..." 看到 activiti 插件:
4、設置保存 bpmn 流程圖時,同時保存一張同名的 .png 圖片:
繪製工作流程圖
1、安裝好插件之後就可以開始繪製工作流程圖了,隨便在某個目錄上右鍵,然後 "New -> Other...-> Activiti -> Activiti Diagram" 創建 Activiti 流程圖:
2、基本工作流程圖繪製並不難,從右側的選項中拖到到中間即可,開始的黑色圓圈爲開始事件,User Task 表示用戶任務,帶黑色叉的矩形爲排它網關(用於條件判斷),末尾的粗黑線圓圈爲結束事件,箭頭線表示順序流。
3、排它網關用於條件判斷,比如同意則繼續往後走、不同意則重新返回,在網關上選擇箭頭按住拖到目標按鈕上即可。
4、在空白處點擊,爲整個流程設置 id 與 name,在每個按鈕上點擊,爲他們設置 id 與 name 值,這些值將來都是 .bpmn 文件中的屬性值,代碼中會操作它們,它們也會被自動處理到數據庫中。
5、爲3個用戶任務設置 form 表單參數,即此任務執行需要的參數,因爲操作比較長,無法全部做成動圖,所以只演示添加1個屬性。type 表示參數類型,required 表示參數是否必須要填。
6、用戶任務設置的參數表示工作流中用戶提供的參數,而這些參數需要在排它網關中進行判斷,比如主管審批結果輸入爲 y 時才能繼續往後走,否則輸入 n 時,則應該重新回到 "填寫審批信息"。當排它網關未作任何限制時,則主管和人事無論輸入什麼,都會繼續往後走,顯然這不滿足我們的需求,則此時應該設置排它網關的條件,它類似 EL 表達式的寫法 ${條件判斷} ,|| 表示或,&& 表示與,條件判斷的值是上一個用戶任務中的 form 表單參數。
7、至此工作流程圖繪製完畢,第一次可能比較難理解,但是多畫幾次,然後結合後面的代碼,則容易理解。
注意:這個流程圖(.bpmn文件)本質就是一個 .xml 文件,可以用記事本、或者瀏覽器等打開就可以看出來,後面會提供完整的文件,所以即使沒畫出來,也沒關係,並不影響後面的代碼運行。
IDEA 編碼實現工作流
1、因爲本人習慣使用 IDEA,所以在 IDEA 中新建一個 Maven 項目,Java SE 應用,直接在 main 方法中進行簡單使用。
2、將 sts 中插件繪製好的流程文件 .bpmn 拷貝到 IDEA 項目的類路徑下,當然直接使用 sts 、eclipse 開發也是一樣的。
3、pom.xml 文件內容如下。
spring-boot-starter-parent 與 spring-boot-maven-plugin 是方便 maven 項目打包的,可選
slf4j-api 與 slf4j-log4j12 用於做日誌框架,可選
activiti-spring-boot-starter-basic:activiti 與 spring boot 集成的啓動器,裏面包含了 activiti-engine 引擎,以及 activiti 與 spring 的集成,以及 jdbc 。必選,
h2 是內存數據庫,必須使用一個數據庫,當然可以換成其它數據庫。
guava 方便做集合、字符串等操作,可選
junit 方便做單元測試,可選
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>wmx.com</groupId>
<artifactId>activiti-first</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.activiti/activiti-engine -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.183</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4、只建了一個 MainApp.java 文件操作工作流,編碼分爲 4 步:1)創建流程引擎 -> 2)部署流程定義文件 -> 3)啓動運行流程、4)處理流程任務。內容如下:
import com.google.common.collect.Maps;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class MainApp {
private static Logger LOGGER = LoggerFactory.getLogger(MainApp.class);
public static void main(String[] args) throws ParseException {
LOGGER.info(" App start...");
//1、創建流程引擎
ProcessEngine processEngine = getProcessEngine();
//2、部署流程定義文件
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
//3、啓動運行流程
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition.getId());
//4、處理流程任務
handlingTasks(processEngine, processInstance);
LOGGER.info(" App quit...");
}
/**
* 處理流程任務
*
* @param processEngine
* @param processInstance
* @throws ParseException
*/
private static void handlingTasks(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
Scanner scanner = new Scanner(System.in);//從控制檯手動輸入數據
while (processInstance != null && !processInstance.isEnded()) {
TaskService taskService = processEngine.getTaskService();//獲取任務服務
List<Task> taskList = taskService.createTaskQuery().list();
for (Task task : taskList) {
LOGGER.info("當前待處理任務id [{}],任務名稱 [{}]", task.getId(), task.getName());
Map<String, Object> dataMap = getStringObjectMap(processEngine, scanner, task);
taskService.complete(task.getId(), dataMap);//提交任務/完成任務
//重新獲取流程實例
processInstance = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstance.getId())
.singleResult();
}
}
scanner.close();
}
/**
* 設置當前任務的表單參數。從控制檯輸入
*
* @param processEngine
* @param scanner
* @param task
* @return
* @throws ParseException
*/
private static Map<String, Object> getStringObjectMap(ProcessEngine processEngine, Scanner scanner, Task task) throws ParseException {
FormService formService = processEngine.getFormService();//表單服務
TaskFormData taskFormData = formService.getTaskFormData(task.getId());//根據任務id獲取任務表單數據對象
List<FormProperty> formPropertyList = taskFormData.getFormProperties();//獲取表單屬性
Map<String, Object> dataMap = Maps.newHashMap();
for (FormProperty formProperty : formPropertyList) {
String line = null;
if (StringFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("請輸入 {}", formProperty.getName());
line = scanner.nextLine();
dataMap.put(formProperty.getId(), line);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("請輸入 {}", formProperty.getName());
line = scanner.nextLine();
dataMap.put(formProperty.getId(), DateUtils.parseDate(line, "yyyy-MM-dd HH:mm:ss"));
} else {
LOGGER.warn("暫時不支持此類型 [{}]", formProperty.getType());
}
LOGGER.info("您成功輸入 [{}]", line);
}
return dataMap;
}
/**
* 啓動運行流程
*
* @param processEngine
* @param processDefinitionId
*/
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, String processDefinitionId) {
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
String processDefinitionKey_ = processInstance.getProcessDefinitionKey();
LOGGER.info("啓動流程,流程key [{}] ", processDefinitionKey_);//輸出:啓動流程,流程key [approvalFor2Level]
return processInstance;
}
/**
* 部署流程定義文件
*
* @param processEngine
* @return
*/
private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
RepositoryService repositoryService = processEngine.getRepositoryService();//獲取存儲服務
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();//創建部署構建器
//通過類路徑下的 .bpmn 文件進行構建。.bpmn 文件的本質其實就是一個定義好的 .xml 文件
deploymentBuilder.addClasspathResource("approvalFor2Level.bpmn");
//部署流程定義文件。此時 Activiti 會自動將 .bpmn 中的數據添加到數據庫中,以備後續數據庫操作
Deployment deployment = deploymentBuilder.deploy();
String deploymentId = deployment.getId();
String deploymentKey = deployment.getKey();
String deploymentName = deployment.getName();
Date deploymentTime = deployment.getDeploymentTime();
//輸出:部署id [1],部署key [null],部署名稱 [null],部署時間 [Wed Jul 24 09:40:00 CST 2019]
LOGGER.info("部署id [{}],部署key [{}],部署名稱 [{}],部署時間 [{}]", deploymentId, deploymentKey, deploymentName, deploymentTime);
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId)
.singleResult();
String processDefinitionName = processDefinition.getName();
String processDefinitionId = processDefinition.getId();
String processDefinitionKey = processDefinition.getKey();
//輸出:流程定義文件名 [二級審批流程],流程id [approvalFor2Level:1:4],流程key [approvalFor2Level]
LOGGER.info("流程定義文件名 [{}],流程id [{}],流程key [{}]", processDefinitionName, processDefinitionId, processDefinitionKey);
return processDefinition;
}
/**
* 創建流程引擎
*
* @return
*/
private static ProcessEngine getProcessEngine() {
//StandaloneInMemProcessEngineConfiguration:標準的基於內存數據庫的流程引擎配置.
ProcessEngineConfiguration pecfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
//buildProcessEngine:通過流程引擎配置構建流程引擎,此時會自動創建 activiti 的 28 張表
ProcessEngine processEngine = pecfg.buildProcessEngine();
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
LOGGER.info("流程引擎名稱 [{}],版本 [{}]", name, version);//輸出:流程引擎名稱 [default],版本 [6.0.0.4]
return processEngine;
}
}
5、整個項目源碼:https://github.com/wangmaoxiong/activitiFirst,核心就是 pom.xml 文件、MainApp.java、approvalFor2Level.bpmn 流程文件、log4j.properties 日誌文件。
1)上面的 hello World 要想成功運行,之前繪製好的 .bpmn 文件是必不可少的,因爲內容較長,所以不再這裏粘貼,從這裏獲取:https://github.com/wangmaoxiong/activitiFirst/tree/master/src/main/resources
2)其中可以修改 log4j.properties 的日誌輸出級別爲 DEBUG log4j.rootLogger=DEBUG ,console,logFile,就會發現應用啓動到結束,它在不停的操作數據庫,做 sql 操作。
6、因爲使用的是 StandaloneInMemProcessEngineConfiguration 流程引擎配置,它的源碼如下,顯然它默認使用的是 h2 內存數據庫,create-drop 表示應用啓動時自動建表,應用關閉時自動刪除,數據不會持久化到磁盤。
public class StandaloneInMemProcessEngineConfiguration extends StandaloneProcessEngineConfiguration {
public StandaloneInMemProcessEngineConfiguration() {
this.databaseSchemaUpdate = "create-drop";
this.jdbcUrl = "jdbc:h2:mem:activiti";
}
}
7、啓動應用,測試如下:
上面只是測試了正常情況下,全部通過的情景,也可以放棄提交,主管或者人事輸入 n 拒絕通過等否則情況都是沒問題的
Activiti 核心 API 預覽
1、ProcessEngine:流程引擎,這是最重要的 API,通過它可以獲取其它的 API。
2、RepositoryService:流程存儲服務,管理流程定義文件 xm 及靜態資源的服務,對特定流程的暫停和激活,流程定義啓動權限管理。
3、RuntimeService:流程運行服務,用於對流程運行過程的控制,如啓動流程實例、暫停、掛起、繼續等
4、TaskService:流程任務服務,用於對人工任務的管理(如增刪改查),設置操作權限。
5、HistoryService:歷史記錄相關服務接口。
6、FormService:表單服務。
7、IdentityService:身份服務,提供對流程角色數據進行管理的 API,這些角色數據包括用戶組、用戶及它們之間的關係。
8、ManagementService:提供對流程引擎進行管理和維護的服務。