一、Activiti介紹
Activiti項目是一項新的基於Apache許可的開源BPM平臺,從基礎開始構建,旨在提供支持新的BPMN 2.0標準,包括支持對象管理組(OMG),面對新技術的機遇,諸如互操作性和雲架構,提供技術實現。
Activiti是一個獨立運作和經營的開源項目品牌,並將獨立於Alfresco開源ECM(企業內容管理)系統運行。Activiti將是一種輕量級,可嵌入的BPM引擎,而且還設計適用於可擴展的雲架構。
官網給出的定義如下:Activiti 是一個針對企業用戶、開發人員 、系統管理員的輕量級工作流業務管理平臺(Business Process Management, 簡稱BPM),其核心是使用 Java 開發的快速 、 穩定的 BPMN2.0 流程引擎 。Activiti是開源的,並基於Apache許可發行。Activiti可以在任何Java應用程序、服務器、集羣或雲中運行,它可以與 Spring 完美集成。
Activiti Github地址:https://github.com/Activiti/Activiti
二、Activiti插件
Activiti要在Eclipse中使用,需要安裝如下插件
三、配置啓動流程
1.引入配置文件
Activiti自帶的表在實際開發中是使用activiti.cfg.xml配置文件生成,在src/test/resources下創建一個xml文件 名字是:activiti.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_activiti" />
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="123456" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
2.獲取流程引擎實例
/**
* 使用xml配置 簡化
*/
@Test
public void testCreateTableWithXml(){
// 引擎配置
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 獲取流程引擎對象
ProcessEngine processEngine=pec.buildProcessEngine();
}
流程引擎配置對象的繼承樹如下:
流程引擎對象默認使用h2內存數據庫
在創建完上面的流程引擎對象後,生成28張數據表
activiti5.19.0.2支持的是25張表
3.使用Eclipse繪製流程圖
啓動流程實例步驟分三步:
1.部署流程定義文件(涉及到資源表,流程部署記錄表,流程定義信息表)
2.啓動流程實例
3.查看流程任務以及完成流程任務
4.部署流程定義文件
public void deploy() {
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.addClasspathResource("diagrams/MyProcess.bpmn")
.addClasspathResource("diagrams/MyProcess.png")
.name("FirstApprove流程")
.deploy();
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
部署完之後,三張表的變化
5.啓動流程實例
//啓動流程實例
@Test
public void start() {
ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(“myProcess”);//通過流程定義表中的key_字段獲取
System.out.println("流程實例ID:"+processInstance.getId());
System.out.println("流程定義ID:"+processInstance.getProcessDefinitionId());
}
表的變化:
6.執行任務
@Test
public void completeTask(){
processEngine.getTaskService() // 任務相關Service
.complete("2505"); // 指定要完成的任務ID
}
表的變化
在歷史表中,包括歷史參與者表、歷史任務表、歷史節點信息表,未完成的任務所涉及的參與者、節點信息都會記錄
7.查看任務
//查看任務
@Test
public void findTask(){
// 查詢並且返回任務即可
List<Task> taskList=processEngine.getTaskService() // 任務相關Service
.createTaskQuery() // 創建任務查詢
.taskAssignee("李四") // 指定某個人
.list();
for(Task task:taskList){
System.out.println("任務ID:"+task.getId());
System.out.println("任務名稱:"+task.getName());
System.out.println("任務創建時間:"+task.getCreateTime());
System.out.println("任務委派人:"+task.getAssignee());
System.out.println("流程實例ID:"+task.getProcessInstanceId());
}
}
當完成最後一個人工任務後,所有的以act_run_開頭的表都置空,但能在歷史信息表中查看到相關信息
四、Activiti的實際應用
Activiti流程定義ZIP部署方式
Activiti支持以壓縮包的形式部署流程定義文件,從而擺脫通過classpath方式加載資源文件的侷限性。
/**
* 使用zip方式部署流程定義文件
*/
@Test
public void deployWithZip() {
InputStream imInputStream = this.getClass().getClassLoader()
.getResourceAsStream("diagrams/MyProcessZipTest.zip");//獲取指定文件資源流
ZipInputStream zipInputStream = new ZipInputStream(imInputStream);//實例化zip輸入流
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.name("MyProcessZip部署")
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
因爲MyProcessZipTest.zip壓縮包中含有兩個文件,bpmn和png,因此上傳到流程引擎中之後,在act_ge_bytearray中多了兩個資源文件,相應的數據表的變化:
Activiti流程定義查詢
- 通過Activiti提供的API 把act_re_procdef表的所有列的數據全部查詢出來,在開發系統的時候管理員、用戶可以通過用戶界面來維護這些數據。
- Activiti提供非常豐富的API,可以做SQL查詢、對某些字段查詢、模糊查詢、分頁查詢和排序等。
/**
* 查詢流程定義,返回流程定義集合
*/
@Test
public void ListAll() {
List<ProcessDefinition> list = processEngine.getRepositoryService()
.createProcessDefinitionQuery()//創建流程定義查詢
.processDefinitionKey("myProcess")
.list();
for(ProcessDefinition pdf : list) {
System.out.println("ID=" + pdf.getId());
System.out.println("Name=" + pdf.getName());
System.out.println("Key=" + pdf.getKey());
System.out.println("Version" + pdf.getVersion());
System.out.println("=================");
}
}
activiti提供的流程定義查詢方法:
Activiti流程定義刪除
- 在開發中肯定會有一些流程不需要了,要刪除,Activiti中也是存在刪除操作的
- 通過流程定義部署ID來執行刪除流程定義
/**
* 刪除流程定義文件,通過流程定義部署ID來進行刪除
*/
@Test
public void deleteProdef() {
processEngine.getRepositoryService().deleteDeployment("15001");
System.out.println("刪除流程定義" );
}
/**
* 級聯刪除,正在運行的流程實例也會刪除,包括歷史記錄表中的信息也會被刪除
*/
@Test
public void deleteProdefForce() {
processEngine.getRepositoryService().deleteDeployment("15001", true);//默認是False,true表示級聯刪除
System.out.println("刪除流程定義成功" );
}
不能刪除正在運行的流程定義文件,有主外鍵關係
Cannot delete or update a parent row: a foreign key constraint fails (db_activiti
.act_ru_execution
, CONSTRAINT ACT_FK_EXE_PROCDEF
FOREIGN KEY (PROC_DEF_ID_
) REFERENCES act_re_procdef
(ID_
))
Activiti查看流程圖片
- 在開發中可能需要查看某個流程的流程圖片,對應操作的數據庫表是act_ge_bytearray的Bytes_字段
- Activiti提供了操作接口,可以查詢返回一個資源文件輸入流,然後就可以得到這張流程圖片保存到本地服務器,然後圖片多在自己的服務器上
需要commons-io依賴
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/**
* 通過流程部署ID獲得流程圖片
* @throws IOException
*/
@Test
public void getImageById() throws IOException {
InputStream inputStream = processEngine.getRepositoryService().getResourceAsStream("1", "diagrams/MyProcess.png");
FileUtils.copyInputStreamToFile(inputStream, new File("D:/MyProcess.png"));
}
Activiti修改流程定義
流程定義按本質來說是不能修改的。所以在開發中,不能修改流程定義,而是通過增加版本號的方式。來實現“修改”的。
每進行一次流程部署,version就會加1,啓動流程實例的時候,是用Key來啓動。這樣啓動的時候就是用的最新版本的流程定義來啓動流程實例。Id值組成是 key值:版本號:流程定義ID
Activiti刪除key相同的流程
一個流程定義不需要的,需要刪除所有版本
把所有Key相同的流程定義批量刪除
1、根據Key查詢所有的流程定義
2、遍歷集合,取得每個流程的部署ID
3、根據流程部署ID即可刪除所有的流程定義
public void deleteProdefBykeyForce() {
List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey("myProcess").list();
for(ProcessDefinition pd : list) {
System.out.println(pd.getDeploymentId());
//processEngine.getRepositoryService().deleteDeployment(pd.getDeploymentId(), true);
}
}
Activiti歷史流程操作
act_hi_procinst表的id和流程實例id始終是一樣的
/**
* 歷史流程實例相關信息查詢
* 根據流程實例ID進行查詢
*/
@Test
public void getHistoryProcessInstance() {
HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId("2501").singleResult();
System.out.println("=======歷史流程實例相關信息查詢===========");
System.out.println("流程實例ID:"+ historicProcessInstance.getId());
System.out.println("流程實例創建時間:"+ historicProcessInstance.getStartTime());
System.out.println("流程實例結束時間:"+ historicProcessInstance.getEndTime());
}
/**
* 歷史任務實例相關信息查詢
* 查詢某個流程定義的所有歷史任務
*/
@Test
public void getHistoryTaskQuery() {
List<HistoricTaskInstance> list = processEngine.getHistoryService().createHistoricTaskInstanceQuery().processDefinitionId("myProcess:1:4").finished().list();
System.out.println("=======歷史任務實例相關信息查詢===========");
for(HistoricTaskInstance his : list) {
System.out.println("任務ID:" + his.getId());
System.out.println("流程實例ID:" + his.getProcessInstanceId());
System.out.println("辦理人:" + his.getAssignee());
System.out.println("================");
}
}
Activiti流程變量設置與獲取
根據流程定義的key啓動一個流程
@Test
public void start() {
ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey("employeeMyProcess");
System.out.println("流程實例ID:" + processInstance.getId());
System.out.println("流程實例創建時間:" + processInstance.getStartTime());
}
設置流程變量
要設置流程變量,需要獲取Service,在這裏,TaskService可以設置變量,RuntimeService也可以設置流程變量
假如節點不是任務節點的時候,只能用RuntimeService。接口和方法和TaskService一樣的;執行設置變量的方法會對應的把數據庫查詢到對應的流程變量表(act_ru_variable)中,當然那個序列化對象,特殊點,存到了字節文件表(act_ge_bytearray)裏去了
@Test
public void setProcVariable() {
String taskId = "27505";
Employee employee = new Employee(1526, "張三");
TaskService taskService = processEngine.getTaskService();
taskService.setVariable(taskId, "date", new Date());
taskService.setVariable(taskId, "reason", "發燒");
taskService.setVariable(taskId, "days", 2);
taskService.setVariable(taskId, "employee", employee);//序列化對象
}
獲取“員工請假提交”任務節點設置的流程變量
@Test
public void getVariables() {
TaskService taskService = processEngine.getTaskService();
String taskId = "32502";
Employee employee = (Employee) taskService.getVariable(taskId, "employee");
Date date = (Date) taskService.getVariable(taskId, "date");
String reason = (String) taskService.getVariable(taskId, "reason");
Integer days = (Integer) taskService.getVariable(taskId, "days");
System.out.println("請假人:" + employee);
System.out.println("請假日期:" + date);
System.out.println("請假原因" + reason);
System.out.println("請假天數:" + days);
}
Activiti連線
完成任務
public class EmplyeeTest2 {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程
@Test
public void deploy() {
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.addClasspathResource("diagrams/employeeMyProcess2.bpmn")
.addClasspathResource("diagrams/employeeMyProcess2.png")
.name("員工請假流程2")
.deploy();
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
//啓動一個流程實例
@Test
public void start() {
ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey("employeeMyProcess2");
System.out.println("流程實例ID:" + processInstance.getId());
System.out.println("流程定義ID:" + processInstance.getProcessDefinitionId());
System.out.println("流程實例創建時間:" + processInstance.getStartTime());
}
//查看任務
@Test
public void findTask(){
// 查詢並且返回任務即可
List<Task> taskList=processEngine.getTaskService() // 任務相關Service
.createTaskQuery() // 創建任務查詢
.taskAssignee("李四") // 指定某個人
.list();
for(Task task:taskList){
System.out.println("任務ID:"+task.getId());
System.out.println("任務名稱:"+task.getName());
System.out.println("任務創建時間:"+task.getCreateTime());
System.out.println("任務委派人:"+task.getAssignee());
System.out.println("流程實例ID:"+task.getProcessInstanceId());
}
}
//完成任務
@Test
public void completeTask() {
processEngine.getTaskService().complete("42503");
}
@Test
public void completeTask2() {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("msg", "重要情況");
processEngine.getTaskService().complete("40002", variables);
}
}
Activiti排他網關
private static String completeTask(String taskID){
String daysValue = "7";
Map<String, Object> variables = new HashMap<>();
variables.put("days",daysValue);
processEngine.getTaskService().complete(taskID, variables);
System.out.println("完成任務:" + taskID);
System.out.println("下一任務:" + processEngine.getTaskService()
.createTaskQuery().singleResult().getName());
return processEngine.getTaskService().createTaskQuery().singleResult().getId();
}
private static void completeTask2(String taskID){
System.out.println("當前任務:" + processEngine.getTaskService()
.createTaskQuery().singleResult().getName());
processEngine.getTaskService().complete(taskID);
System.out.println("完成任務");
}
Activiti並行網關
private static void completeTask(String taskID){
processEngine.getTaskService().complete(taskID);
System.out.println("完成任務:" + taskID);
List<Task> taskList = processEngine.getTaskService().createTaskQuery().list();
for(Task task : taskList){
System.out.println("任務名稱:" + task.getName());
System.out.println("任務委任人:" + task.getAssignee());
System.out.println("=================");
}
}
private static void completeTask2(){
List<Task> taskList = processEngine.getTaskService().createTaskQuery()
.processInstanceId("90005").list();
if (taskList.size()>0){
for(Task task : taskList){
System.out.println("任務名稱:" + task.getName());
System.out.println("任務委任人:" + task.getAssignee());
processEngine.getTaskService().complete(task.getId());
System.out.println("完成任務:" + task.getName());
}
}
}